-
-
Notifications
You must be signed in to change notification settings - Fork 94
Developer Resources
The mod can manually installed using the built in scripts. The following steps will guide you though this. Please note: You will need to have Visual Studio 2017 & Cities: Skylines installed, be running Windows 10 and have developer mode enabled. This script will automatically pull in the required files (after specifying a folder), build the mod and then install it.
- Open the
scripts
folder. - Run the following command in powershell
.\build.ps1 -Update -Build -Install
. This will match the mod to your game, build it and then install it. - When you run this script, it will ask you for your steam folder. This is just the root folder of steam, e.g 'C:\Program Files\Steam'
- Run Cities: Skylines and enable the mod. The mod can also be built and installed when the game is running (in most cases).
If you have access to the main project, create a new branch for your feature, work on it and then create a pull request when complete (or a draft pull request if still in development). Before a pull request can be merged, it must be approved, builds must run and it must be based off the latest code change to avoid conflicts).
If you don't have access to the main project, fork this project, make your changes and then create a pull request like above.
- Create a new class under the
CSM.Commands
namespace (src/Commands/Data
folder) with a suffux ofCommand
(see other commands as an example). - Adjust the class to extend
CommandBase
. Implement the class level attribute of[ProtoContract]
. - Create your getters and setters, these should all be public and contain public level get and set, e.g.
public Vector3 Position { get; set; }
. - Annotate your getters and setters using
[ProtoMember(int)]
. Start at 1 and work your way up. - Make sure you document your getters/setters and class.
- Create a new class under the
CSM.Commands.Handler
namespace (src/Commands/Handler
folder) with a suffix ofHandler
(see other handlers as an example). - Adjust this class to extend
CommandHandler<COMMAND>
where COMMAND is your newly created command. - Override other methods and implement logic.
This mod uses the client-server model. A user will setup their game as a server and transmit information like currency, roads, needs etc. to all connected clients. Clients will connect to the server and retrieve currency, roads, needs etc. from the server, updating the client UI.
This is all done by running a UDP server alongside Cities Skylines. This UDP server will interact with the extension methods in the ICities DLL. It is important that we extend and override the base methods for these extensions (as we override some values).
Below is information that I have jotted down about the flow of this mod.
Server:
- Open a level (new or existing).
- "Show Multiplayer Menu" --> "Host Game". User enters port and password (optional).
- Server is setup and message process queue is started. (
Networking/Server.cs
)
Message Queue:
- Parse incoming messages and call appropriate event handlers (UpdateEconomy etc.)
- On extension changes, send a packet to all clients.
Client:
- User launches game, enabled mod, loads a level.
- Click "Show Multiplayer Menu" --> "Join Game".
- Enter game IP address / port.
- Unload the level
- Connect the client using
Networking/Client.cs
. - Client connects to server (try) and performs setup functions (check mods same etc.)
- Update the client to mirror the server
- On all events, update the server.
- On incoming message, update client UI.
Here is a list of extensions that can be used in the ICities.dll (not much documentation elsewhere).
We can use these extension methods to sync which areas have been bought.
Method | Return |
---|---|
OnCanUnlockArea(int x, int z, bool originalResult) |
originalResult |
OnGetAreaPrice(uint ore, uint oil, uint forest, uint fertility, uint water, bool road, bool train, bool ship, bool plane, float landFlatness, int originalPrice) |
originalPrice |
OnUnlockArea(int x, int z) |
VOID |
We can use this to find out where builds are. Looks like it may be quite complicated. Guess we will see.
Method | Return |
---|---|
SpawnData OnCalculateSpawn(Vector3 location, SpawnData spawn) |
spawn |
OnBuildingCreated(ushort id) |
VOID |
OnBuildingRelocated(ushort id) |
VOID |
OnBuildingReleased(ushort id) |
VOID |
This really is not that important, (I personally don't really like the "Twitter" like feature), but it can still be implemented. On Server we send the new chirper message to the clients on OnNewMessage
event. Client side we ignore these chirpers.
Method | Return |
---|---|
OnNewMessage(IChirperMessage message) |
VOID |
This extension will allow us to synchronize demand across all connected clients. More research is required, but from what I understand, we need to override the OnCalculate*Demmand
methods to grab the calculated demand from the server. On the server we will access the demand manager to get the current demand. The OnUpdateDemand
method will also be used for server-client syncing.
Method | Return |
---|---|
OnCalculateResidentialDemand(int originalDemand) |
originalDemand |
OnCalculateCommercialDemand(int originalDemand) |
originalDemand |
OnCalculateWorkplaceDemand(int originalDemand) |
originalDemand |
OnUpdateDemand(int lastDemand, int nextDemand, int targetDemand) |
nextDemand |
Currently looking at implementing OnUpdateMoneyAmount
to sync money between clients. Some basic testing showed that this was only updating the UI? Need to look further into it.
Method | Return |
---|---|
OnAddResource(EconomyResource resource, int amount, Service service, SubService subService, Level level) |
amount |
OnFetchResource(EconomyResource resource, int amount, Service service, SubService subService, Level level) |
amount |
OnPeekResource(EconomyResource resource, int amount) |
amount |
OnGetConstructionCost(int originalConstructionCost, Service service, SubService subService, Level level) |
amount |
OnGetMaintenanceCost(int originalMaintenanceCost, Service service, SubService subService, Level level) |
amount |
OnGetRelocationCost(int constructionCost, int relocationCost, Service service, SubService subService, Level level) |
amount |
OnGetRefundAmount(int constructionCost, int refundAmount, Service service, SubService subService, Level level) |
amount |
OnUpdateMoneyAmount(long internalMoneyAmount) |
internalMoneyAmount |
This has a different naming scheme for some reason? This would be used for syncing disasters (going to be interesting to setup)
Method | Return |
---|---|
OnDisasterCreated(ushort disasterID) |
VOID |
OnDisasterStarted(ushort disasterID) |
VOID |
OnDisasterDetected(ushort disasterID) |
VOID |
OnDisasterActivated(ushort disasterID) |
VOID |
OnDisasterDeactivated(ushort disasterID) |
VOID |
OnDisasterFinished(ushort disasterID) |
VOID |
todo
todo
todo
todo
todo
todo
todo