Advertisement

Various questions regarding to MMORPG's.

Started by March 11, 2018 01:22 PM
6 comments, last by Januario 6 years, 8 months ago

Hey!

Awesome community right here, I'm pretty new, hopefully, we'll get along together :)

Here we go:

1) I've been wanting to learn GoLang for a while and I've been involved in some sandboxes lately, so I thought, why not? Let's make a basic game as a learning experience and I thought about doing the server in GoLang, what is your opinion on this? (The server is pretty much a web-app, could possibly build an app for it too in the future).

2) Regarding to networking, just readed an answer from hplus0603 suggesting to stay away from socket.io and WebSockets, even though the suggestion was made for a fighting game, I find it as a good signal to ask this question, in fact, I'm playing a game right now that uses WebSockets and the connection is honestly not as good, should I stay away from it too?, is there any architecture you'd advise for this?.

3) Regarding to server-scripting, in the future, it would be good to outsource it and let other people do the job, so I'll have to eventually add some kind of scripting to the game. How hard is it to implement LUA scripting in a GoLang engine? Is this just too much?

4) Lastly, a living map. A problem I've frequently encounter in MMORPGs that I've been playing on the web (indie games) is that they deal very bad with complex living maps and creatures, sometimes it looks so bad that I literally think that the server is doing a loop through all the monsters and check if any of those can attack a nearby player or something (it really feels that bad, monsters re-load the pathfinder like once a second, which is too slow...).

Not only this issue regarding creatures but also the map, let's say you'd have a living map (grounds decaying, plants appearing, etc...) which is pretty common in sandbox games... which would be the proper way to reload all this? how should I keep track and update every single tile in an asynchronous way?

And that's it for now!

I'll be anxiously awaiting for your answers, opinions, and suggestions!

Thank you so much in advance :D 

1) When learning a new language, it's typically a good idea to do a project you already know how to do in another language, so that you don't have to learn both what to do, and how to do it, at the same time.

2) WebSockets and socket.io, and all other web/TCP based networking (HTTP2 streaming etc) are all pretty bad for real-time games. If you want real-time, then you want UDP, which means you either don't use a web browser, or you hack something crazy on top of WebRTC, which in itself is a big undertaking in any language.

3) Golang can call into "foreign functions" written in C, and Lua is written in C, so it's totally possible. Whether you'd get to the point where your game is good enough that other people would write scripts for it is another question. Also beware: Lua is not thread safe. There are other external scripting options, including forking a sub-process that you talk to using pipes, or starting a separate script interpreter over a network interface, or using shared memory for communication, so don't think that you HAVE to embed a scripting language, or choose only one language. It all depends on what your particular needs are.

4) That sounds entirely like a world design and implementation / algorithm question, not a language specific question. Every game has to make some trade-offs between costs (hardware resources, implementation time, skill of people involved) and features/polish. Companies like Blizzard do what they do so well because they can basically pick and choose between many of the best game programmers in the world, and they have very large budgets. Small indie games don't have that option, and are often built by people earlier in their career, who are not as experienced in where things can go wrong and how to avoid it.

Good luck on your project!

enum Bool { True, False, FileNotFound };
Advertisement

Thank you so much for answering!

Regarding to question 2 and 3, is all clear now!, however, this being just a learning experience I'd like to share it with a community that I got already, which being kinda sandboxing project, I'm sure they'd be glad to test and follow, so it really needs to be on the web, so, having the options that I already got, are all pretty much the same? or is there any you'd recommend specifically?

1) Yup! I've built games in the past, but more importantly worked deeply on the core of some private-servers (written in C++) which gave me an idea about how to structure this whole thing, but this being a more specific game, as you say in #4, there are some specifics I need to resolve now...

... Like the map thing one. I understand the current scenario filled with variety, but could you please name a few methods I can search and get into? is going to be a 2d sandbox probably with different maps through the Z axis (pretty much like pokemon), so, I've been suggested two options but I just can't believe these are the only ones, they sound so bad! :P 

a) Store every single tile on the DB then do queries every X amount of time checking all the tiles and the last-decay time, or whatever last-update effect you are trying to do, then proceed to do the changes on the tile if required. This sounds sooo heavy for the server and gives me the feeling that there will be constant lag-spikes on every round.

b) Make every tile an object and just start dealing with the effects through individual timers on object creation. 

What do you think of those? are these really the only ones we've got?, if so, which would you recommend?. It would be good to hold between 100 and 300 players online (per server).

Thank you again for everything man, that was a nice answer and you're doing the same in every post :D?

If you have to be on the web, then websockets are the least bad option, IMO.

Databases should not be involved in the game loop. You can use a database to load data for a particular player when the player logs in, and to checkpoint data to every once in a while. Preferably, you'd do the actual database I/O in some other thread, or asynchronously, so you don't block the main game thread with lots of slow operations. (In fact, all I/O should be asynchronous and/or on a separate thread.)

I'd suggest, instead of saying "Z axis" for multiple maps, just call them "multiple maps." They may have different map IDs. When moving from one map to another, place a portal that internally has the information "this portal takes you to position X, Y on new map ID I."

If you don't support instancing; all players on a map can see each other; then most robust is to build one process per map. When travelling, the client connects to some other process (on some other IP address or port, perhaps.) Alternatively, use a front-end proxy which sends the player to the correct map process based on where they're at (based on a cookie, or some connection parameter, or whatnot, depending on your implementation.)

Once you have a process that runs all entities within a given map, the structure of that is no different from any other game. Either you do the object mess and "tick" each "entity object" to figure out what it should do, or you make objects compose a number of sub-behaviors, and let each "behavior manager" iterate over its own "domain of objects" every so often to evolve the data. The benefit of the former is that it keeps behavior in each object. The benefit of the later is that it caches better and generally performs better.

Key is to not re-do work more than once. For example, if you have subsystems like path finding and attack logic and magic effects and physics, that all want to know "what things are within X tiles of me," then  you want to start your iteration by finding things within X distance of each entity, and re-use that discovered state across each of those subsystems for that turn/tick/iteration. Similarly, path finding between larger chunks of tiles can be done once, and stored as a graph; path finding for an individual object then just becomes "find the exit of the current chunk" and follow the canned path from then on. (Until you're inside the same chunk as the target destination.)

 

enum Bool { True, False, FileNotFound };

Sweet!

So, in a summary:

- For the browser, stay with TCP because UDP will require way too much tweaking and weird stuff.

- Keep small maps separated using Z axis (or maps_id, which in theory is pretty much the same).

- "Cache" as much as possible when you are loading map entities and use what you have for subsystems, only reload if required.

But how I should deal with tiles is still confusing for me.

Let´s say I have a 100x100 map with living grass, water, dirt that comes into grass, and so forth...

When loading the map, should I create AND "cache" (keep on memory server, not in the DB) every single tile as an object?

So basically my 100x100 map is an array of tile objects that themselves have their own "type" and so forth...

Inside every single object, they are running their own "ticks" to know if they should decay, turn into something else or whatever...

And of course items and players can interact with these objects restarting the ticks, changing the type or whatever, but yeah, is just an array of 100x100 objects.

Despite it sounding like a lot of objects and ticking inside, it sounds MUCH more efficient than looping every X seconds between ALL the tiles and checking if a tile needs to decay (change the type) or whatever... correct?

So, is this how it should be (as the first array of objects method suggests)? or is there another way I'm not talking about here? :P 

When loading the map, should I create AND "cache" (keep on memory server, not in the DB) every single tile as an object?

You should absolutely keep your entire level in memory, not in the database, yes.

Whether that's "as an object" or "as an array of structs" or "as five different arrays, each keeping a separate property about the tile in question" is entirely up to you. I listed them in order of most convenient to least convenient, but also slowest to fastest.

class Tile {
  TileType *type;
  int woodResources;
  int oreResources;
  int waterLevel;
  int fireAmount;
};
	Tile **theMap = new Tile*[100*100];
for (int y = 0; y != 100; ++y) {
  for (int x = 0; x != 100; ++x) {
    theMap[y*100+x] = new Tile();
  }
}
	

struct Tile {
  TileType *type;
  int woodResources;
  int oreResources;
  int waterLevel;
  int fireAmount;
};
Tile **theMap = new Tile[100 * 100];
for (int y = 0; y != 100; ++y) {
  for (int x = 0; x != 100; ++x) {
    init_tile(&theMap[y*100+x]);
  }
}
	

TileType **theMapTileType = new TileType *[100*100];
int **theMapWoodResources = new int[100*100];
int **theMapOreResources = new int[100*100];
int **theMapWaterLevel = new int[100*100];
int **theMapFireAmount = new int[100*100];
for (int y = 0; y != 100; ++y) {
  for (int x = 0; x != 100; ++x) {
    init_tile_yx(y, x);
  }
}

 

enum Bool { True, False, FileNotFound };
Advertisement

You are incredibly awesome man... 

THANK YOU! 

:D:D:D

This topic is closed to new replies.

Advertisement