bbuck / dragon-mud

A text-based game engine written in Go and scripted with Lua.
Other
116 stars 16 forks source link

Remove support for selecting databases and build for one. #24

Closed bbuck closed 7 years ago

bbuck commented 7 years ago

So this one has required quite a bit of thought. I'll try and start from where I first began to where I finally made the decision.

When implementing the support for optional database support I watched the significant binary growth (not a major concern, but a minor one) due to the addition of more than one database connection library being part of the project, only one of which could be used at any one time. So I chose SQLite due to how easy it easy it is to configure and use although single access to the file could prove problematic later in development, but I wanted to nearly remove the barrier to entry for using this program. Then I chose to support MySQL, which I'm personally pretty burnt out on, because of it's popularity among the more experienced types. And then I chose PostgreSQL (at the time when I implemented this support) because it's my personal favorite.

Then comes the thoughts on random values for players. So I'm not sure how other systems handle this and it's probably a good idea to look but I don't care too much to do that research and I'm not really willing to have a "random values" table that's like player_id, value_name, value and them being strings, or finding some convoluted way to store the value like 10/number or having a value_type column in addition (and then possible a value_types table that relates too...). PostgreSQL has a great jsonb type with indexing and searching capabilities. Using it as a dynamic storage field I don't see leverage indexing but it could be great searching for a user with some random value, like phase values for certain rooms: select * from players where values->'forest_phase' = 'final'.

Anyways, aside from all that hooplah I've decided that using the jsonb type of PostgreSQL and not hacking or downgrading support into some text field just so I can use two database types with little-to-no JSON support (or similar). It's important to understand this is simply a decision based on how I want to handle "extra" values. With an "infinite" number of quests/phased rooms/instance locks and other random things like maybe randomly locking a user into only using an item once or drinking a potion once or opening something once or whatever. This a place to store values that may or may not grow or drastically change over time.

In addition to this change I will also be rolling out gorm support for a more raw Postgres connector that will leverage and support JSON/JSONB. This is a big change, or at least someone, as basic structure will be changing and a new migration methodology will have to be applied and implemented as well. I have some ideas already on how to handle this implementation that I've used in the past, although it's a totally new approach from "magic" fetching to more specific and less dynamic (which I can be okay with, depending on needs). I'm sure some form of and ORM needs to be implemented. But for the most part querying the database only needs to happen in server scripts not in-game scripts. So I think a more limited support here is fine. In-game scripts just need to be able to set values, probably something like player:SetGameValue("crystal_forest.phase", "growing") or something.

By now I'm rambling, so this is probably the best place to stop.

bbuck commented 7 years ago

After that very long and eloquent story I've reached an odd thought. Instead of traditional RDBMS systems why not go for something a little more free form like NoSQL. Sure NoSQL has it's own problems, but in a "moldable" platform like a MUD this eschews the need for formal migration processes and allows some content to be added dynamically.

Other things that it does is simplify some queries that become difficult in a RDBMS environment (queries that follow many and multiple relationships) such as, as a simplistic example, the "yell" functionality. I've always implemented yell to reach connected rooms up to 5 hops from the current room. I've always implemented this in a recursive fashion, to provide an example:

function perform_yell(player, message)
  yell_in_room(player:Room(), player, message, [], 0)
end

function yell_in_room(room, player, message, visited, distance)
  room:BroadcastMessage(player:display_name .. " yells " .. message)
  exits = room:Exits()
  table.insert(visited, room.id)
  for i = 1, #exits do
    if not utils.ListContains(visited, exits[i]:To().id)
      yell_in_room(exits[i]:To(), player, message, visited, distance + 1)
    end
  end
end

This is an odd recursive implementation, but with a GraphDB and quick and easy queries, MATCH (r:Room {id: 1})-[:EXITS_TO*1..5]->(r2:Room) RETURN r, r2 could return all rooms we need.

function perform_yell(player, message)
  rooms = rooms.FindRoomsByDistanceFrom(player:Room().id, 5)
  for i = 1, #rooms do
    rooms[i]:BroadcastMessage(player.display_name .. " yells " .. message)
  end
end

And then the problem is simplified. Also weather systems and what not for some pretty insane fancy features like shortest path between two rooms or other graph related searched.

bbuck commented 7 years ago

As of now this branch has balooned into a massive re-direction for the project. I'll restructure branches/PRs when the time comes.

bbuck commented 7 years ago

The work in this branch has now been moved into new-direction. Details coming with new issue.