Merge: concurrent_collections: Add implementation of has method
[nit.git] / contrib / nitrpg / src / game.nit
index 4d62283..5413f43 100644 (file)
@@ -25,7 +25,7 @@
 # the `GameReactor` abstraction.
 module game
 
-intrude import json::store
+import mongodb
 import github::events
 
 # An entity within a `Game`.
@@ -35,23 +35,21 @@ interface GameEntity
        # The game instance containing `self`.
        fun game: Game is abstract
 
-       # Uniq key used for data storage.
-       fun key: String is abstract
+       # Collection `self` should be saved in.
+       fun collection_name: String is abstract
 
-       # Saves the `self` state in game data.
-       #
-       # Date are stored under `self.key`.
-       fun save do game.store.store_object(key, to_json)
+       # Uniq key of this entity within the collection.
+       fun key: String is abstract
 
-       # Saves `self` state into `target` key data.
-       #
-       # Data are stored under `target.key / self.key`.
-       fun save_in(target: GameEntity) do
-               game.store.store_object(target.key / key, to_json)
-       end
+       # Saves `self` in db.
+       fun save do game.db.collection(collection_name).save(to_json_object)
 
        # Json representation of `self`.
-       fun to_json: JsonObject  do return new JsonObject
+       fun to_json_object: JsonObject do
+               var json = new JsonObject
+               json["_id"] = key
+               return json
+       end
 
        # Pretty print `self` to be displayed in a terminal.
        fun pretty: String is abstract
@@ -65,33 +63,51 @@ class Game
 
        redef fun game do return self
 
-       # Returns the repo `full_name`.
-       #
-       # Example: `"privat/nit"`
-       redef fun key do return repo.full_name
-
        # We need a `GithubAPI` client to load Github data.
        var api: GithubAPI
 
        # A game takes place in a `github::Repo`.
        var repo: Repo
 
-       # Directory where game data are stored.
-       var game_dir: String is lazy do return "nitrpg_data" / repo.full_name
+       # Game name
+       var name: String = repo.full_name is lazy
 
-       # Used for data storage.
-       #
-       # File are stored in `game_dir`.
-       var store: JsonStore is lazy do return new JsonStore(game_dir)
+       redef var key = name is lazy
+
+       # Mongo server url where this game data are stored.
+       var mongo_url = "mongodb://mongo:27017" is writable
+
+       # Mongo db client.
+       var client = new MongoClient(mongo_url) is lazy
+
+       # Mongo db name where this game data are stored.
+       var db_name = "nitrpg" is writable
+
+       # Mongo db instance for this game.
+       var db: MongoDb is lazy do return client.database(db_name)
+
+       redef var collection_name = "games"
 
        # Init the Game and try to load saved data.
-       init do if store.has_key(key) then from_json(store.load_object(key))
+       init from_mongo(api: GithubAPI, repo: Repo) do
+               init(api, repo)
+               var req = new JsonObject
+               req["name"] = repo.full_name
+               var res = db.collection("games").find(req)
+               if res != null then from_json(res)
+       end
 
        # Init `self` from a JsonObject.
        #
        # Used to load entities from saved data.
        fun from_json(json: JsonObject) do end
 
+       redef fun to_json_object do
+               var json = super
+               json["name"] = name
+               return json
+       end
+
        # Create a player from a Github `User`.
        #
        # Or return the existing one from game data.
@@ -112,10 +128,12 @@ class Game
        # Returns `null` if the player cannot be found.
        # In this case, the player can be created with `add_player`.
        fun load_player(name: String): nullable Player do
-               var key = "players" / name
-               if not store.has_key(key) then return null
-               var json = store.load_object(key)
-               return new Player.from_json(self, json)
+               var req = new JsonObject
+               req["name"] = name
+               req["game"] = game.key
+               var res = db.collection("players").find(req)
+               if res != null then return new Player.from_json(self, res)
+               return null
        end
 
        # List known players.
@@ -124,12 +142,12 @@ class Game
        #
        # To add players see `add_player`.
        fun load_players: MapRead[String, Player] do
+               var req = new JsonObject
+               req["game"] = game.key
                var res = new HashMap[String, Player]
-               if not store.has_collection("players") then return res
-               var coll = store.list_collection("players")
-               for id in coll do
-                       var name = id.to_s
-                       res[name] = load_player(name).as(not null)
+               for obj in db.collection("players").find_all(req) do
+                       var player = new Player.from_json(self, obj)
+                       res[player.name] = player
                end
                return res
        end
@@ -148,7 +166,7 @@ class Game
        end
 
        # Erase all saved data for this game.
-       fun clear do store.clear
+       fun clear do db.collection(collection_name).remove(to_json_object)
 
        # Verbosity level used fo stdout.
        #
@@ -180,8 +198,8 @@ end
 class Player
        super GameEntity
 
-       # Key is based on player `name`.
-       redef var key is lazy do return "players" / name
+       # Stored in collection `players`.
+       redef var collection_name = "players"
 
        redef var game
 
@@ -195,6 +213,8 @@ class Player
        # The name is also used to load the user data lazilly from Github API.
        var name: String
 
+       redef var key = name is lazy
+
        # Player amount of nitcoins.
        #
        # Nitcoins is the currency used in nitrpg.
@@ -212,13 +232,13 @@ class Player
        #
        # Used to load players from saved data.
        init from_json(game: Game, json: JsonObject) do
-               self.game = game
-               name = json["name"].to_s
+               init(game, json["name"].as(String))
                nitcoins = json["nitcoins"].as(Int)
        end
 
-       redef fun to_json do
+       redef fun to_json_object do
                var json = super
+               json["game"] = game.key
                json["name"] = name
                json["nitcoins"] = nitcoins
                return json
@@ -292,6 +312,19 @@ end
 
 # utils
 
+# Sort games by descending number of players.
+#
+# The first in the list is the game with the more players.
+class GamePlayersComparator
+       super Comparator
+
+       redef type COMPARED: Game
+
+       redef fun compare(a, b) do
+               return b.load_players.length <=> a.load_players.length
+       end
+end
+
 # Sort players by descending number of nitcoins.
 #
 # The first in the list is the player with the more of nitcoins.