collinsmith / riiablo

Diablo II remade using Java and LibGDX
http://riiablo.com
Apache License 2.0
884 stars 101 forks source link

separate Player entity from save file data #35

Closed collinsmith closed 5 years ago

collinsmith commented 5 years ago

Player has become a mess and some of the code needs to be rearranged. Save specific data like hotkeys, hireling and actions don't really belong in the Player entity class. Most of this data needs to be moved to GameScreen or a new container class created similar to Riiablo.client except the client in this case refers to the game client's active player (e.g., Riiablo.player). Player should essentially just be the entity itself, so equipping an item would modify the player entity state to now draw that item, and the other classes like InventoryPanel and StashPanel would use the Riiablo.player reference instead of the game entity. The other benefit of doing this is that it would probably make implementing multiplayer players easier since only a subset of their info is ever sent to the other clients anyways, thus I could reduce the number of Player constructors and special considerations for non-local player entities.

Additionally, I would really like to support doing something like viewing the items of the players in your current game (possibly of players not in your game from the lobby even). This would either mean that some of their data is downloaded to the client (so each client would contain a list of the other players' equipped items), or I separate this logic from the game code and make this it's own packet which returns the player's equipped items. I don't know how this would effect set bonuses though, so maybe those would need to be tacked onto the packet along with the player's level.

collinsmith commented 5 years ago

I guess I should explain why this is a problem and why the separation is needed. Technically single player has access to the character save file D2S, and so far I've been using that as an argument in the constructor for constructing the Player entity. The issue is that multiplayer never has access to this data, at least in its entirety, so I can't just download the D2S onto every client -- which would create a stateful nightmare. So the goal should be that client's should be able to run with or without their character's D2S, and that should exist on the server only for multiplayer.

I'm not 100% sure how I want to handle listen server games, I'm thinking other players would need to upload their D2S to the host, or host updates would cause clients to write the changes to their D2S, but I'm not sure how to handle the authoritative nature of this architecture. I.e., I don't know how much checking should be done if say a host sends updates which breaks a client's save file. It could also be that all client's in a listen server game are their own hosts and are sending updates to each other as if in a regular dedicated server multiplayer game.

collinsmith commented 5 years ago

D2S should also support loading header only (170 or 765 bytes -- 171-765 contains map, merc, quest, waypoint, npc, stats which may not all be needed for character select screen and menus). Not loading after 765 alone would improve the loading time of these screens since the items aren't needed.

collinsmith commented 5 years ago

I implemented deferred loading of non-header data in D2S. Header data includes up to 0x14F. Most of this data will be used in Riiablo.player, whether loaded from a D2S or received over the network. Connecting to your network account will transmit the headers for your characters so the client can render and either connecting or joining a game will transmit the remaining info.

collinsmith commented 5 years ago

I created CharData (Riiablo.charData) which will be a container for save data, either from a local D2S or over the network. This class will also support generating a D2S in the case of single player (multiplayer will generate the D2S on the server and transmit to player). I had to disable some of the networking code temporarily, which is fine because that package is due to be replaced. Next step is to replace the coupling with gameScreen.player with Riiablo.charData for all of the save data accessors. I may also remove/refactor some of the Player class stuff like Player.Stats and Player.Skills to support the other changes and prepare for things like items adding skills to players.

It also may be necessary to keep a reference to the local client's Player entity easily available to do things like use warps or some skills.

collinsmith commented 5 years ago

I refactored in CharData. Tests so far indicate success. I've decided to keep item details always based on the local player.

I wanted to note also, D2S.StatData has been modified so that the float fields are just their unencoded int values. This is to keep these fields in line with the other stats.