From fd4e85f38100a48b0138aa1357036692a9c54741 Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Wed, 24 Jun 2015 22:40:43 -0400 Subject: [PATCH] nitrpg: migrate data management from `json_store` to `mongodb` Signed-off-by: Alexandre Terrasa --- contrib/nitrpg/src/achievements.nit | 43 +++++++------ contrib/nitrpg/src/events.nit | 23 ++++--- contrib/nitrpg/src/game.nit | 78 +++++++++++++---------- contrib/nitrpg/src/statistics.nit | 45 +++++++------ contrib/nitrpg/src/templates/templates_base.nit | 7 +- 5 files changed, 112 insertions(+), 84 deletions(-) diff --git a/contrib/nitrpg/src/achievements.nit b/contrib/nitrpg/src/achievements.nit index 5ce317a..c1a8036 100644 --- a/contrib/nitrpg/src/achievements.nit +++ b/contrib/nitrpg/src/achievements.nit @@ -32,22 +32,28 @@ redef class GameEntity # # TODO should update the achievement? fun add_achievement(achievement: Achievement) do - var key = self.key / achievement.key - if game.store.has_key(key) then return stats.inc("achievements") - achievement.save_in(self.key) - save + achievement.owner = self + achievement.save end + # Is `a` unlocked for this `Player`? + fun has_achievement(a: Achievement): Bool do return load_achievement(a.id) != null + # Load the event from its `id`. # # Looks for the event save file in game data. # Returns `null` if the event cannot be found. fun load_achievement(id: String): nullable Achievement do - var key = self.key / "achievements" / id - if not game.store.has_key(key) then return null - var json = game.store.load_object(key) - return new Achievement.from_json(game, json) + var req = new JsonObject + req["id"] = id + req["game"] = game.key + req["owner"] = key + var obj = game.db.collection("achievements").find(req) + if obj isa JsonObject then + return new Achievement.from_json(game, obj) + end + return null end # List all events registered in this entity. @@ -56,12 +62,13 @@ redef class GameEntity # # To add events see `add_event`. fun load_achievements: MapRead[String, Achievement] do + var req = new JsonObject + req["game"] = game.key + req["owner"] = key var res = new HashMap[String, Achievement] - var key = self.key / "achievements" - if not game.store.has_collection(key) then return res - var coll = game.store.list_collection(key) - for id in coll do - res[id.to_s] = load_achievement(id.to_s).as(not null) + for obj in game.db.collection("achievements").find_all(req) do + var achievement = new Achievement.from_json(game, obj) + res[achievement.id] = achievement end return res end @@ -74,10 +81,11 @@ end class Achievement super GameEntity + redef var collection_name = "achievements" redef var game - redef var key is lazy do + redef fun key do var owner = self.owner if owner == null then return id return "{owner.key}-{id}" @@ -121,12 +129,6 @@ class Achievement end redef class Player - - # Is `a` unlocked for this `Player`? - fun has_achievement(a: Achievement): Bool do - return load_achievement(a.id) != null - end - # Unlocks an achievement for this Player based on a GithubEvent. # # Register the achievement and adds the achievement reward to the player @@ -140,6 +142,7 @@ redef class Player nitcoins += a.reward add_achievement(a) trigger_unlock_event(a, event) + save end # Create a new event that marks the achievement unlocking. diff --git a/contrib/nitrpg/src/events.nit b/contrib/nitrpg/src/events.nit index bd1ee58..2573a58 100644 --- a/contrib/nitrpg/src/events.nit +++ b/contrib/nitrpg/src/events.nit @@ -35,13 +35,12 @@ redef class GameEntity # # To add events see `add_event`. fun load_events: Array[GameEvent] do - var key = key / "events" + var req = new JsonObject + req["game"] = game.key + req["owner"] = key var res = new Array[GameEvent] - if not game.store.has_collection(key) then return res - var coll = game.store.list_collection(key) - for id in coll do - var name = id.to_s - res.add load_event(name).as(not null) + for obj in game.db.collection("events").find_all(req) do + res.add new GameEvent.from_json(game, obj) end (new EventTimeComparator).sort(res) return res @@ -52,10 +51,13 @@ redef class GameEntity # Looks for the event save file in game data. # Returns `null` if the event cannot be found. fun load_event(id: String): nullable GameEvent do - var key = key / "events" / id - if not game.store.has_key(key) then return null - var json = game.store.load_object(key) - return new GameEvent.from_json(game, json) + var req = new JsonObject + req["game"] = game.key + req["owner"] = key + req["internal_id"] = id + var res = game.db.collection("events").find(req) + if res != null then return new GameEvent.from_json(game, res) + return null end end @@ -63,6 +65,7 @@ end class GameEvent super GameEntity + redef var collection_name = "events" redef var game diff --git a/contrib/nitrpg/src/game.nit b/contrib/nitrpg/src/game.nit index 2924e46..9b550c3 100644 --- a/contrib/nitrpg/src/game.nit +++ b/contrib/nitrpg/src/game.nit @@ -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 under `key` data. - # - # Data are stored under `key / self.key`. - fun save_in(key: String) do - game.store.store_object(key / self.key, to_json) - end + # Saves `self` in db. + fun save do game.db.collection(collection_name).save(to_json) # Json representation of `self`. - fun to_json: JsonObject do return new JsonObject + fun to_json: 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 @@ -74,18 +72,29 @@ class Game # Game name var name: String = repo.full_name is lazy - # Directory where game data are stored. - var game_dir: String is lazy do return "nitrpg_data" / repo.full_name - redef var key = name is lazy - # Used for data storage. - # - # File are stored in `game_dir`. - var store: JsonStore is lazy do return new JsonStore(game_dir) + # Mongo server url where this game data are stored. + var mongo_url = "mongodb://localhost: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 do + 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. # @@ -118,10 +127,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. @@ -130,12 +141,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 @@ -154,7 +165,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) # Verbosity level used fo stdout. # @@ -186,6 +197,9 @@ end class Player super GameEntity + # Stored in collection `players`. + redef var collection_name = "players" + redef var game # FIXME contructor should be private diff --git a/contrib/nitrpg/src/statistics.nit b/contrib/nitrpg/src/statistics.nit index 153b5cd..0bdb421 100644 --- a/contrib/nitrpg/src/statistics.nit +++ b/contrib/nitrpg/src/statistics.nit @@ -34,11 +34,6 @@ redef class Game redef var stats is lazy do return new GameStatsManager(game, self) - redef fun save do - super - stats.save_in(self.key) - end - redef fun pretty do var res = new FlatBuffer res.append super @@ -46,17 +41,17 @@ redef class Game res.append stats.pretty return res.write_to_string end + + redef fun save do + super + stats.save + end end redef class Player redef var stats is lazy do return new GameStatsManager(game, self) - redef fun save do - super - stats.save_in(self.key) - end - redef fun nitcoins do return stats["nitcoins"] redef fun nitcoins=(nc) do stats["nitcoins"] = nc @@ -67,6 +62,11 @@ redef class Player res.append stats.pretty return res.write_to_string end + + redef fun save do + super + stats.save + end end # Store game stats for defined period. @@ -99,12 +99,15 @@ class GameStatsManager # Load statistics for a `period` key. fun load_stats_for(period: String): GameStats do - var key = owner.key / self.key / period - if not game.store.has_key(key) then + var req = new JsonObject + req["period"] = period + req["owner"] = owner.key + var obj = game.db.collection("statistics").find(req) + if obj isa JsonObject then + return new GameStats.from_json(game, period, owner, obj) + else return new GameStats(game, period, owner) end - var json = game.store.load_object(key) - return new GameStats.from_json(game, period, owner, json) end redef fun [](key) do return overall[key] @@ -133,12 +136,12 @@ class GameStatsManager weekly.dec(e) end - redef fun save_in(key) do - overall.save_in(key / self.key) - yearly.save_in(key / self.key) - monthly.save_in(key / self.key) - daily.save_in(key / self.key) - weekly.save_in(key / self.key) + redef fun save do + overall.save + yearly.save + monthly.save + daily.save + weekly.save end redef fun pretty do return overall.pretty @@ -151,6 +154,8 @@ class GameStats redef var game + redef var collection_name = "statistics" + # The period these stats are about. var period: String diff --git a/contrib/nitrpg/src/templates/templates_base.nit b/contrib/nitrpg/src/templates/templates_base.nit index a4f40d9..470160b 100644 --- a/contrib/nitrpg/src/templates/templates_base.nit +++ b/contrib/nitrpg/src/templates/templates_base.nit @@ -21,8 +21,11 @@ import achievements redef class GameEntity + # Path to this entity from root. + fun path: String do return collection_name / key + # URL to this game entity page. - fun url: String do return game.url / key + fun url: String do return game.url / path end redef class Game @@ -32,7 +35,7 @@ redef class Game # This must be set before any access to `url`. var root_url: String is noinit, writable - redef fun url do return "{root_url}/games" / key + redef fun url do return "{root_url}/{path}" # Return a HTML link to this Game. fun link: String do return "{name}" -- 1.7.9.5