Kryonet + Libgdx, keeping a list of players on server + client

Hey there, I’m building an online rpg game where there are ~100 npcs, and I’d love to have 1000 players(don’t know the limits of kryonet). Anyway, I finally got the networking to work somewhat but my problem begins when taking connection.getID() on the server starts from 1, whereas client.getID() on the client starts at 0, simple enough to subtract 1 but I’m working in the 1000s and it gets confusing very fast.

Currently I generate the world on the server with actors 0-99 being npcs, then 100-999 are empty player classes that I update when a player connects/moves. On the client I have the same, with a stage with 0-99 being empty npcs off the map, when the server connects it tells the client to move all the npcs to their locations. Same with players. This feels super wrong generating 1000 actors when the game starts rather than dynamically creating and removing them.

The only reason I can’t do it right is I don’t know how to keep connection ids and stage actors related. Currently to update the servers actors im just doing stage.getActors().items[connection.getID()] etc, but if I remove a player the whole stage gets mixed up. How do I do this right??

My only idea is to give the player an id field and assign it with it’s connection id when it’s created. This seems exploitable and I can’t see it being efficient to search the whole list of actors each time I want to reference 1 player.

As for the limit of players, Kryonet won’t be the bottleneck factor. The limit is caused by a combination of factors, such as the hardware specifications and requirements, amount of data that needs to be transferred, connection speed and of course netcode quality. If your netcode is written poorly, then it’s asking for trouble and you won’t have a very high limit. Besides, a limit can be a bit subjective. I’m sure there won’t be a 1000 players at the same area at any given instance. The players will be scattered around the map, doing their own thing. Someone at the very east of the map won’t affect anyone at the far west of the map, and vice versa.

If you seperate different areas and instance them on different pieces of hardware, the performance will increase. Only the parts that need to communicate with eachother do so. It’s very important to only send information that someone needs, the rest will be a waste of bandwidth and processing power.

The id problem shouldn’t be very hard to solve. Instead of the ids being the index of the actors, keep a list of ids and create actors accordingly. That way makes sure that when you remove a player/id, the whole thing doesn’t mix up and collapse. Like you said, assign the connection id to the connected player and keep it in a list.

LostWarrior

Incase anyone stumbles upon this, my solution is as follows:

The server has 2 hashmaps, 1 hashmap of Players, and 1 hashmap of Instances.

The Instance class has 3 hashmaps, NPCs, Players, and WorldObjects. Each of these have their own class extended from Actor. When the server is started, it loads the overworld instance data from a file, the file holds information on what npcs exist, where they are, etc, as well as which worldobjects exists, what type(tree, rock, etc) and where they are as well. When a player joins the server they are added to the server Player hashmap, as well as the overworld instance player hashmap. Clients can then request to join instances by sending an instancepacket which basically just contains the requested instance id. The server checks if that instance is loaded on the server and if not, it loads it and adds the player to the hashmap, otherwise it simply adds the new player to the hashmap. The server is on a 1/60f tick rate and each tick it loops over the loaded instances and calls their act() function which calls the act() function on each actor in the hashmaps.

The client has an instance variable and as such, only has 1 instance at a time. When they join the server their instance is set to overworld and they send the server a request for that instance info. The server sends them all the npcs, all the players, and all the worldobjects which the client renders. The server also sends the new players information to all players in the same instance.

The benefit of hashmaps is that they are unordered so they are simple to put and get data with O(1) timing. If anyone has any suggestions to this system I’d love to hear them. The hashmap key is the players connectionID