SFML community forums

Help => General => Topic started by: StDH on October 18, 2014, 11:04:06 am

Title: Game Server Structure
Post by: StDH on October 18, 2014, 11:04:06 am
Hello, after long time  ;D .

I want help because i've got problem with my game-server code structure (c++) VS2013.

#I'm getting quite pissed off when:
 - i want to let ent/npc get some data from map,
 - exchanging data between two maps

@ because the structure looks like this:

Main - calls funtion Server::run() to prepare an initialize enviroment.
   |
Server - everything what this does is controling FPS and calls MapMgr::update()
   |
MapMgr - there is std::map<enum, std::unique_ptr<Map>> to store maps & updates them.
   |
Map - std::deque<std::unique_ptr<EntActor>> storing npcs & in update() calling ptr -> update();
   |
EntActor - whole interface, move, goto, health, shield, speed .....

EntActor::update()
moves/rotates entity to specified location, or try attack any near player by data from Map.

and this whole system is one-way, should i make some sort of a global-message-bus ?
Please tell me if you have better idea :) .

# Problem begins when i want to port/move EntActor from map1 to map2.
Title: Re: Game Server Structure
Post by: eXpl0it3r on October 18, 2014, 11:34:14 am
So you really think these few keywords will explain everything to us?
Title: Re: Game Server Structure
Post by: StDH on October 18, 2014, 11:35:26 am
okay, i'll try to "explain" more

EDIT: i hope it's better now :) .
Title: Re: Game Server Structure
Post by: Xornand on October 18, 2014, 05:21:46 pm
A simple solution could be to access the map directly - for example, by creating a Service Locator (http://gameprogrammingpatterns.com/service-locator.html) as a singleton, which would hold a pointer to the map (or an array of maps).

Also, I have two questions after looking at how you store your maps and entities.
Title: Re: Game Server Structure
Post by: StDH on October 19, 2014, 11:31:15 am
Xornand: thanks :) it helped a lot.

And that std::vector, well i never used it because it reallocate everything when added something new but when i read your post then i reallized this in my mind :D : (f**k, i use only static amount of maps and npcs....., he's right)  ;D
Title: Re: Game Server Structure
Post by: Nexus on October 19, 2014, 05:14:29 pm
You should always use std::vector unless you have a reason for a different data structure. It performs well in a wide variety of scenarios.

std::array is a possibility if the number of elements is known at compile time, such as the number of enumerators in an enum. You can then use the idiom of a separate Count enumerator to avoid magic constants:
namespace Scope
{
    enum MyEnum
    {
        Enumerator1,
        Enumerator2,
        ...
        Count // <- always last one
    };
}

std::array<MyElem, Scope::Count> array;

Alternatively, you can use C++11 scoped enums (enum struct or enum class), but then you need an explicit type cast to integers.
Title: Re: Game Server Structure
Post by: StDH on October 20, 2014, 06:29:28 am
I've already done this couple of minutes ago before i read your post :D :

managerMaps.cpp
Maps::Maps()
{
        m_maps.reserve(global::MAP::MapsAmount);
}

m_maps is a std::vector

but thanks :) .
Title: Re: Game Server Structure
Post by: Gambit on October 20, 2014, 07:55:37 am
You need a form of persistance to keep data from the server stored between sessions. When you change map, generally you will be starting a new local server which (obviously) will not have the same variables and stuff as the previous one. This is easily solved by writing the relevant persistant data (health, armour, weapons, etc) to a file before the map changes, then reading it when the next map is loaded.
Title: Re: Game Server Structure
Post by: StDH on October 20, 2014, 04:11:23 pm
It's not "local" game, it's multiplayer, so maps are paused except map/s where is/are player/s.
Title: Re: Game Server Structure
Post by: Gambit on October 20, 2014, 09:19:44 pm
Its still the same thing. You save persistent data, close the server, open a new one, load the new map, load the persistent data. This is how most games do it like Half Life and Counter Strike (even Quake did it this way).
Title: Re: Game Server Structure
Post by: StDH on October 20, 2014, 09:56:50 pm
i'm not quite sure what do you want to tell me with that :D
Title: Re: Game Server Structure
Post by: Gambit on October 21, 2014, 08:44:07 am
I'm simply pointing out that its a common method and it works. You could even use a database depending on which way you want to go but a file is probably safer and maybe faster in the long run.

Simply put, persistent data is data that will not change between sessions. If you have 10 health and leave the game, restart the server or change the map, when you come back to that game you will still have 10 health.

I'm suggesting that you do it the way that Quake and Valve's games do it which works and its a simple approach.
Title: Re: Game Server Structure
Post by: StDH on October 21, 2014, 09:10:39 am
i dont use any DB/File, intead i use computer memory:

databaseNPC, databaseMap: (example)
enum iType
{
        A = 0,
        B,
        C
}

struct iStruct
{
        int a;
        int b;
        int c;
}

static std::map<enum, const iStruct> m_db;

funcs:
static void initialize(); // to add all npcs .or. maps to db
static const iStruct get(const enum);
 

i use this for npcs & maps that's the reason of std::map
Title: Re: Game Server Structure
Post by: Xornand on October 21, 2014, 09:54:03 am
I think you didn't understand what Gambit was talking about. Storing everything in RAM doesn't make your data persistent - it'll be gone the moment your std::map is destroyed (either by going out of scope or by closing your application, for example due to a server reboot).

Now, depending on your game, this may be or not a desired behavior. If your game is like an MMO or needs to keep players' stats, then you'll want state persistence, so saving the game state to a file or a database is the way to do it. However, if each multiplayer session is unique and you don't care about tracking any information about the players (and thus everyone always starts from the beginning and the entire map is reloaded every time a new game is hosted), then I guess you could get away without the database.

From your vague description, it seems like you're making a persistent world (at least that's the impression I got), so if that's the case, you'll need some persistent storage for storing your persistent data and storing it in RAM doesn't solve the problem.
Title: Re: Game Server Structure
Post by: StDH on October 21, 2014, 10:46:40 am
now i understand.

Well, static things like Hitpoints,Shield,Wapons are in application/ram, and (as you said) stats will be in database (persistent)
Title: Re: Game Server Structure
Post by: Gambit on October 21, 2014, 11:45:00 pm
Things like weapons and items are a tad different, they are usually hard coded or created at run time with a script language. They are nor persistent or temporary (unless of course thats how you want them).

Also be careful about using strings in persistent data if you are using a database as you are prone to injections (especially if you are persisting strings that the user can change like names).
Title: Re: Game Server Structure
Post by: StDH on October 22, 2014, 06:19:34 am
i dont use strings anywhere (except player name or some text in game) even npc name is an enum in server.
About changing string-names in client, first way to "protect" it, is using char[] but still... not enough.

EDIT:

so i assume theese are the most suitable containers for my needs.

std::vector<std::unique_ptr<entity::Actor>> m_npcs;
std::deque<std::unique_ptr<entity::Actor>> m_players;

std::vector = there is only static amount of npcs that respawns on death
std::deque = because player can change his/her map, so i need to delete in middle
Title: Re: Game Server Structure
Post by: Gambit on October 22, 2014, 08:59:33 am
I havent used queues in C++ at all but as far as char[] being secure? I'm not sure where you got that from.
Title: Re: Game Server Structure
Post by: StDH on October 22, 2014, 09:09:57 am
i meant to not let them make the string longer.
Title: Re: Game Server Structure
Post by: Nexus on October 22, 2014, 10:52:19 am
i meant to not let them make the string longer.
Against who are you trying to protect exactly?

What Gambit meant are SQL injections (https://en.wikipedia.org/wiki/SQL_injection). But at the moment, you definitely have more important things to care about :)
Title: Re: Game Server Structure
Post by: Gambit on October 22, 2014, 03:37:20 pm
Yes I meant SQL injections, thank you for clarifying that Nexus. "Protecting" a string length against users using a char array is old and C. I hope you are checking for overflows.
Title: Re: Game Server Structure
Post by: StDH on October 22, 2014, 03:41:09 pm
i'm still at beginning, have to make fully working BOT system then add players, so protecting/securing code phase will be at the end.

EDIT:

ToDo List:
let npc be aware of map.
AI + they should attack each other (not in real game)
data echange between maps (for players)
Title: Re: Game Server Structure
Post by: Nexus on October 22, 2014, 03:42:36 pm
"Protecting" a string length against users using a char array is old and C.
It makes no sense whatsoever. How can a user (not a developer) simply resize a std::string but not a dynamic char array (it must be dynamic if it should be able to store variable length names)?

i'm still at beginning, have to make fully working BOT system then add players, so protecting/securing code phase will be at the end.
Exactly. Before worrying about crazy scenarios, you should get your basic mechanics working... With questionable protection mechanisms you're just wasting your time.
Title: Re: Game Server Structure
Post by: Xornand on October 22, 2014, 04:20:42 pm
so i assume theese are the most suitable containers for my needs.

std::vector<std::unique_ptr<entity::Actor>> m_npcs;
std::deque<std::unique_ptr<entity::Actor>> m_players;

std::vector = there is only static amount of npcs that respawns on death
std::deque = because player can change his/her map, so i need to delete in middle

If you're deleting stuff in the middle of a container, you'd be better off using std::list - after all, that's what it's for.
Title: Re: Game Server Structure
Post by: Nexus on October 22, 2014, 04:37:33 pm
Not necessarily. std::vector is in most cases considerably more efficient than std::list. It also uses far less memory, especially when elements are so tiny (sizeof(std::unique_ptr) is 4-8 bytes).

In std::vector containers, algorithms like std::remove_if() can remove elements in a relatively efficient way, and the swap()-and-pop_back()-idiom even allows to remove a middle element in constant time, as long as the element order can be changed.

I have used std::list mainly when indirections (pointers, references, iterators) to elements must remain valid. std::deque for queue-like structures where elements are often inserted and removed at both ends, or if I want containers to grow without invalidating existing pointers. Otherwise always std::vector for sequential containers, it should be the default choice.
Title: Re: Game Server Structure
Post by: Xornand on October 22, 2014, 05:20:08 pm
Yes, std::vector might prove to be cheaper if you're only copying objects of the size of integers and are not storing tens of thousand of elements. That's a special case though.

Generally, using a linked list is preferred if you're removing very often from the middle - and especially if you're also storing a large amount of elements.

That being said, I think it's best to measure it yourself. It may very well turn out that in this specific case using std::vector will be cheaper - if not, then using std::list should be your next choice - but then again, it all boils down to how you're planning to be using the data after you store it (i.e. how often you will iterate, perform random accesses and removals in the middle).
Title: Re: Game Server Structure
Post by: Gambit on October 22, 2014, 07:01:31 pm
"Protecting" a string length against users using a char array is old and C.
It makes no sense whatsoever. How can a user (not a developer) simply resize a std::string but not a dynamic char array (it must be dynamic if it should be able to store variable length names)?

Huh? What do you mean? I was simply saying that explicitly declaring char[] to hold a string is old and C-style and that std::string should be preferred since it is dynamically allocated and you dont have to worry about buffer overflows.
Title: Re: Game Server Structure
Post by: MorleyDev on October 22, 2014, 07:26:00 pm
Generally, using a linked list is preferred if you're removing very often from the middle - and especially if you're also storing a large amount of elements.

Actually you'd be surprised. Whilst theoretically list does do the removal quicker, the search for what to remove is still O(N) for both vector and list, and vector has better cache coherence so performs this search significantly faster due to less cache misses. Fast enough that vector can outperform list overall, simply because all those cache misses add up.

It's this reason that even Bjarne (https://www.youtube.com/watch?v=YQs6IC-vgmo) himself advocates viewing vector as the default container, and only using a specialisation like list or set when needed (i.e you need iterates to remain valid after a remove) or when profiling shows you that the removal is costing performance and that the specialisation does indeed overall out-perform a vector.
Title: Re: Game Server Structure
Post by: Xornand on October 22, 2014, 07:48:36 pm
Generally, using a linked list is preferred if you're removing very often from the middle - and especially if you're also storing a large amount of elements.

Actually you'd be surprised. Whilst theoretically list does do the removal quicker, the search for what to remove is still O(N) for both vector and list, and vector has better cache coherence so performs this search significantly faster due to less cache misses. Fast enough that vector can outperform list overall, simply because all those cache misses add up.

It's this reason that even Bjarne (https://www.youtube.com/watch?v=YQs6IC-vgmo) himself advocates viewing vector as the default container, and only using a specialisation like list or set when needed (i.e you need iterates to remain valid after a remove) or when profiling shows you that the removal is costing performance and that the specialisation does indeed overall out-perform a vector.
I'm not entirely sure what's your point because I've already said that using std::vector for storing integer-sized objects will likely be cheaper than using an std::list. I read Bjarne's article about std::list in which he was basically encouraging people to think twice before using it because of the very same reason mentioned in the posts above - and I totally agree with him.

Besides, I know that the time complexity for finding the element is the same for both a linked list and an array - and that's why I said that it's always best to measure. Because the thing is, that adding an element in the middle of std::vector is another O(N) in the worst case - and if you're storing a lot of large objects, then copying/moving them will likely diminish any benefit of having the L1/L2 cache-friendliness of an array - and in fact would make linked list a much better solution.

That's also the reason I said that a lot depends on what you'll be doing with the data because if you only add/remove from the middle occasionally, then you probably won't gain much from using std::list - but if it's something that is done every game frame and especially on a container that is supposed to be storing large objects, then that's a different story.
Title: Re: Game Server Structure
Post by: MorleyDev on October 22, 2014, 08:48:28 pm
Wasn't a critique, more meant as an additional bit of info :) The you was meant to be a general you, not you you. Kinda confusing, I could of worded it better. As in "Actually this can be surprising, I find it rather interesting". Sorry that intent wasn't clear ^^
Title: Re: Game Server Structure
Post by: Xornand on October 22, 2014, 09:36:04 pm
It wasn't clear to me, thus my clarifying answer... but it's no problem, I didn't take it as offensive. Cheers ;)
Title: Re: Game Server Structure
Post by: StDH on October 23, 2014, 05:51:05 am
That's also the reason I said that a lot depends on what you'll be doing with the data because if you only add/remove from the middle occasionally, then you probably won't gain much from using std::list - but if it's something that is done every game frame and especially on a container that is supposed to be storing large objects, then that's a different story.

Players are allowed to jump thru portal every 5 seconds, so that's the reason of std::deque.
std::list is bit useless because it's doubly-linked to object in front & back, but yeah it's effecient for large objects
Title: Re: Game Server Structure
Post by: Xornand on October 23, 2014, 09:36:08 am
Players are allowed to jump thru portal every 5 seconds, so that's the reason of std::deque.
IMHO that's a bad reason. std::deque is used for fast insertions and deletions at the front and the back... but you said, you'd be removing from the middle. If that's the case, then std::deque is a poor choice and instead, as mentioned above it'd be more efficient to either stick with a linked-list or (in your particular case, because you're really just storing pointers) a standard std::vector.

std::list is bit useless because it's doubly-linked to object in front & back, but yeah it's effecient for large objects
Could you elaborate?
Title: Re: Game Server Structure
Post by: StDH on October 23, 2014, 12:13:49 pm
std::list has big memory usage.
std::vector is reallocating in every insertion.

std::deque is perfect to store players.
Title: Re: Game Server Structure
Post by: Strelok on October 23, 2014, 01:00:08 pm
Reallocating the vector (you can always reserve) is usually faster than having to read all the pointers to the back of the list (a lot of cache misses)
Title: Re: Game Server Structure
Post by: StDH on October 23, 2014, 01:47:25 pm
std::vector::reserve = pain in multiplayer map without limit, this is just for npcs (static amount)
std::deque = can insert/delete in middle, that's what i need for players ofc.
Title: Re: Game Server Structure
Post by: Nexus on October 23, 2014, 02:46:36 pm
Huh? What do you mean?
I meant using char arrays to protect strings against resizing makes no sense whatsoever, it's not just a bad choice because it's C ;)

Actually you'd be surprised. Whilst theoretically list does do the removal quicker, the search for what to remove is still O(N) for both vector and list, and vector has better cache coherence so performs this search significantly faster due to less cache misses. Fast enough that vector can outperform list overall, simply because all those cache misses add up.
I cannot emphasize this enough. A lot of people who decide between STL containers only look for asymptotic time complexities and forget about the whole other range of factors which are at least as important.

Just use std::vector and change the container if you encounter problems. When there are few elements (up to hundreds or even thousands), everything is blazingly fast so that you don't even care about linear search. StDH, apparently you missed my advice concerning efficient removal in std::vector. Removing elements in the middle alone is not a reason to choose std::list (let alone std::deque, where it's not even more efficient in theory).