ZaneDubya / MedievaLandsPublic

MedievaLands [closed source] is a a recreation of an early MMORPG. The desktop client runs on .NET Framework on Windows and macOS. The server runs on .NET Core on Linux. The website is written in PHP and runs on Apache on Linux. 126,500 lines of C#.
8 stars 1 forks source link

Make CommunityKit generic. #529

Closed ZaneDubya closed 4 years ago

ZaneDubya commented 5 years ago

TL;DR: What if CommunityKit could run other games in the MedievaLands series? What if CommunityKit could run brand new Yserbiusy games?


What would it take to make the CommunityKit engine generic, so that it could support multiple games? Right now both the client and server are set up to only handle data and events for one game at a time. This extends to everything from the resources loaded and the resource provider classes to the mobile/item classes and the map scripts and map blocking. Of course, this makes perfect sense so long as the engine is only handling a single game. But what if...

I'm not going to do this. But it's fun to think about.

Naming things is hard

I realize that the way I have named things in CommunityKit is inconsistent. I'm going to use these keywords moving forward:

Code is named [Generic/Common/Game]ClassName

Assumptions

CommunityKit can run a specific type of game, not all games. The games it can run must fit the following assumptions:

Open questions...

Server

CommunityServer.Legacy.Battle

CommunityServer.Legacy.Chat

CommunityServer.Legacy.Entities

CommunityServer.Legacy.Localization

CommunityServer.Legacy.Maps

CommunityServer.Legacy.Network

CommunityServer.Legacy.Packet

CommunityServer.Legacy.Systems

CommunityServer.Server.Accountings.Account

CommunityServer.Server.Config.MapLoader

CommunityServer.Server.Social.Boards

CommunityServer.Server.Social.Guilds

ZaneDubya commented 5 years ago

Splitting out common routines from map class; now separated into GameCommon.Maps.AMap and scripting in Legacy.Maps.YsMap.

n.b. I am not going to finish this.

ZaneDubya commented 5 years ago

Buffs are now generic. The base class is ABuff. Legacy game buffs inherit from YsBuff.

Right now I'm focused on tearing apart the server. The client will have to wait for the moment.

n.b. I am not going to finish this.

ZaneDubya commented 5 years ago

Working on Player / ServerPlayer and Monster / ServerMonster.

For player objects, there is a shared BasePlayer class that works in all games, which is inherited by a shared GamePlayer class, which is then inherited by a server-only GamePlayerServer class. All the GamePlayerServer classes will also have a component that contains all the server communication code; this component will be common for all server player classes.

For monster objects, there is a shared BaseMonster class that works in all games, which is inherited by a shared GameMonster class. GameMonster is inherited on the server by GameMonsterServer, each instance of which will have a shared server management component.

Items are entirely up in the air at this point.

n.b. I am not going to finish this.

ZaneDubya commented 5 years ago

Tearing apart the Skill and Spell systems. These must be made generic, but are called directly by the common map class, which means I need to rearchitect, at least in part, how skills and spells are used in explore mode. Difficult.

n.b. I am not going to finish this.

ZaneDubya commented 5 years ago

Really struggling with inheritance vs composition here.

The core issue is I want to share the Player and Monster classes between the Client and Server. There is a shared codebase that has a BasePlayer object. BasePlayer has a name, GUID, and hookups for client/server interaction. This is inherited (also in the shared codebase) by a GamePlayer object that has game-specific logic.

Then I want to inherit these on the server by a GamePlayerServer class. But the GamePlayerServer class requires a ton of code which should be shared between all the games. So I'm stuck here.

n.b. I am not going to finish this.

ZaneDubya commented 5 years ago

I think it is important that players from multiple games be able to see each other's information, even if they are not in the same game. To this end I'm writing a portrait renderer on the server. Not sure about the size of the portraits - they need to be small, but I also want to allow for upscaled graphics. Yserbius style portraits are 64x56 pixels (scaled to approximately 64x67), which would be 10.5KB @ 42bpp. I can't make these TOO big... but what if I allowed for a max of 128x128x24bpp - that would be 48kb. I could compress them after rendering. Not sure how small that would make them - would that be manageable?

ZaneDubya commented 5 years ago

BasePlayer / YsPlayer are complete, I think, although I'll need to write a translation routine from the old Server.Legacy.Entities.ServerPlayer class to the new Server.Legacy.Entities.ServerPlayer class, as the serialization routines are completely different. On to monsters!

n.b. I am not going to finish this.

ZaneDubya commented 5 years ago

BaseMonster and YsMonster are complete, I think.

Next up is tearing apart ServerPlayer into things that can go into YsPlayer and things that need to be components and things that should be moved to a YsPlayerHandler static class.

n.b. I am not going to finish this.

ZaneDubya commented 5 years ago

Hit a MAJOR stumbling block in decomposing ServerPlayer. Might need to think about this one for a while.

n.b. I am not going to finish this.

ZaneDubya commented 4 years ago

Dreamed a solution for ServerPlayer. Let's see if this works.

ZaneDubya commented 4 years ago

Have hit a complete dead end with current strategy. Changing things up - let's try to compose the player/monster/item classes out of partial source files...

n.b. I am not going to finish this.

ZaneDubya commented 4 years ago

Well, I figured out how to do this. Abstract partial classes.

But now I have to redo all the work I had previously done.

n.b. I am not going to finish this.

ZaneDubya commented 4 years ago

This is hard. But doable.

I'm starting over from scratch, and am proceeding very, very slowly as I rewrite the shared Yserbius classes and systems with partial classes. Who said you can't do composition in c#?

(I know, I know. Not the same thing.)

n.b. I am probably not going to finish this.

ZaneDubya commented 4 years ago

Slow and steady.

ZaneDubya commented 4 years ago

Buffs are now generic.

Right now I am working on Player / Monster split. Moving code between ServerPlayer and GenericPlayer and the new YsPlayer. What goes in GenericPlayer? What goes in GenericMonster?

ZaneDubya commented 4 years ago

Almost done (?) with Item / Monster / Player split. Careful. Slow. Deliberate.

n.b. I am probably not going to finish this.

ZaneDubya commented 4 years ago

Working on Serialization / deserialization of Player object and a new legacy load feature... if I can get this working, I think this would be good for the day.

ZaneDubya commented 4 years ago

Ok! Server working perfectish with GenericPlayer, now working on client being able to load the correct data depending on the game being played.

ZaneDubya commented 4 years ago

Remaining client references to GenericPlayer where a YsPlayer object is expected included in this comment. Note that this is not enough to make the client generic - this will be just enough to get it compiling with the new GenericPlayer class.

Note: any classes I modify to require an explicit reference to YsPlayer are getting a new Ys prefix. All the Ys/Tw/Cw legacy games should be able to use Ys-prefixed classes.

Player Data (General)

ZaneDubya commented 4 years ago

Idea - all game-specific systems should be referenced through the player object, as the player object is the ONLY class that knows what game it is playing.

Also - how much of the UI can I actually expect to use for games other than Ys/Tw/Cw? I expect I will be more or less rewriting these from scratch for new games........

ZaneDubya commented 4 years ago

I am cheating a bit and adding this to AModeCharEdit:

    /// <summary>
    /// Gets the current player object as type YsPlayer. Should each game have its own version of this?
    /// </summary>
    internal YsPlayer GetPlayer() => Service.Get<LegacyClient>().GetPlayer() as YsPlayer;

I think I am either going to have to have a completely different AModeCharEdit for each game, or will need to have a separate GetPlayer for each game.

Certainly for new games I will need to completely rewrite the UI in addition to all functions that work with player objects. But for the client, at least, I should be able to keep 99% of the code/UI in all cases. So I should be careful about adding Yserbius-specific code when my goal should be to make this generic.

I need to keep my eye on the prize. The goal is not to make this generic right now, it is to make it as generic as possible, get it compiling and bug free, and then locate those specific functions that need special care to be generalized.

ZaneDubya commented 4 years ago

OK! CommunityClient is compiling once more. I need to do a quick round of testing and bug fixing in the new generalized client/server, make sure everything is hooked up and working, and then I will close this issue as complete and move on to #531 to start figuring out how I will need to break apart the Yserbius-specific code from the classes that will also now need to run Twinion and Cawdor. As I do this, I will keep a holistic view of what I would need to do to add completely new games to the engine.

n.b. Things are looking good, but I might still not finish this.

ZaneDubya commented 4 years ago

30 minutes of bug fixing to get the gallery loading! Ooof.

ZaneDubya commented 4 years ago

Faces load in gallery. Data is not being exchanged with the tavern, so everyone looks like a default Yserbius player - that would be a female level 0 human barbarian who follows harmony. I need to refigure the ECharacterSerialization enumeration and Serialization/Deserialization methods in the Player classes so they send appropriate data to the tavern.

ZaneDubya commented 4 years ago

Fixed! All the data seems to be serialized/deserialized correctly. At least when there's only one player... who knows what multiplayer testing will reveal.

ZaneDubya commented 4 years ago

Diving into the server codebase again. I need to eliminate the ServerPlayer class and move its data / methods to the GenericPlayer / YsPlayer classes, see #532

ZaneDubya commented 4 years ago

6,538 lines changed thus far.

ZaneDubya commented 4 years ago

The entire codebase has been significantly altered at this point. My best guess is I'm at 31,040 lines added or changed.

ZaneDubya commented 4 years ago

Closing, although Twinion is not playable, the things covered in this issue are 90% done...