-
-
Notifications
You must be signed in to change notification settings - Fork 39
Home
Welcome to the wiki of HKMP! This wiki will mostly contain information regarding the HKMP API.
The core idea of the API is to expose internal workings of HKMP to so-called "addon". This will allow developers to create integrations of existing mods with HKMP or create new specific features that are not necessarily fit to be included in the HKMP codebase. Addons are divided in two parts, namely "client addons" and "server addons" and are similar to Hollow Knight mods in that they are assemblies that are loaded and can interact with another codebase. In this case, the client and server addons are loaded separately by the client-side and server-side of HKMP and can then interact with their respective APIs (namely, the client API and server API). This is important to keep in mind, because it means that you can be either developing for the client-side, the server-side of both sides of HKMP in terms of addons. The reason for having this divide between client and server addons is basically due to the existence of the standalone server. The standalone server has no notion of the game code, because it runs independently of Hollow Knight, but it may still be interesting to have a server addon that solely interacts with the server API. Addons that require networking are allowed a channel over which they can communicate with their respective counterpart using the existing HKMP network implementation. This means that in order to use networking in your addons, you will need to develop both a client and a server addon. These addons can then communicate with each other to relay important information.
The first thing to do when trying to develop an addon for HKMP is to create a new C# class library. The framework you should be targeting is .NETFramework v4.7.2
, similar to Hollow Knight mods. Next, you should add the reference to the HKMP DLL (HKMP.dll
) in your project. The latest DLL can be found on the releases page.
Then, we can start writing code. In order for HKMP to recognise your class library as an HKMP addon, you'll need to extend either the ClientAddon
or the ServerAddon
class. See the code snippets below for an example:
using Hkmp.Api.Client;
namespace ExampleAddon {
public class ExampleClientAddon : ClientAddon {
public ExampleClientAddon(IClientApi clientApi) : base(clientApi) {
}
public override void Initialize() {
Logger.Info(this, "Initializing client-side example addon!");
}
protected override string Name => "ExampleAddon";
protected override string Version => "0.0.1";
public override bool NeedsNetwork => true;
}
}
using Hkmp.Api.Server;
namespace ExampleAddon {
public class ExampleServerAddon : ServerAddon {
public ExampleServerAddon(IServerApi serverApi) : base(serverApi) {
}
public override void Initialize() {
Logger.Info(this, "Initializing server-side example addon!");
}
protected override string Name => "ExampleAddon";
protected override string Version => "0.0.1";
public override bool NeedsNetwork => true;
}
}
These classes are the starting point of your addon and will allow you to access the respective API. The client and server APIs are passed as a parameter through the constructor, but they are also accessible as a protected
member variable in the ClientAddon
and ServerAddon
classes.
The APIs offer the addons a way to communicate with their respective counterpart through the existing HKMP networking. This is discussed in more detail and with examples below.
To be able to use HKMP networking, you will need to develop both a client and a server addon. These addons will be communicating with each other by sending data to each other and registering handlers for receiving data.
First of all, make sure that the Name
and Version
properties in both classes that extend ClientAddon
and ServerAddon
match. Also, make sure that NeedsNetwork
is true
, otherwise you will not be able to access the networking classes needed.
Let's start with sending data (which is bit more straightforward than receiving). Using the client API (IClientApi
) you can obtain a network sender (IClientAddonNetworkSender
). To do this however, you need to provide an enum type that contains all possible packet ID values. For example, consider the following:
// Enum for client to server communication
public enum ServerPacketId {
PacketId1,
PacketId2
}
This enum type contains two values: PacketId1
and PacketId2
, which can be used to identify the type of data we are sending to the server. Now we can obtain a network sender as follows:
// The client addon instance
ClientAddon clientAddon = ...;
// The client API
IClientApi clientApi = ...;
var netSender = clientApi.NetClient.GetNetworkSender<ServerPacketId>(clientAddon);
Using this network sender we can start sending data to the server (if we are connected of course). The data we want to send should be a class that extends IPacketData
:
public class ServerPacketData : IPacketData {
// Denote whether this data should be handled as reliable
public bool IsReliable => true;
// Whether to drop data if a newer version of the data is also included in the packet
public bool DropReliableDataIfNewerExists => true;
// The data we are transmitting
public float SomeFloat { get; set; }
public void WriteData(IPacket packet) {
packet.Write(SomeFloat);
}
public void ReadData(IPacket packet) {
SomeFloat = packet.ReadFloat();
}
}
This class contains two methods for writing and reading the data to and from the packet. It also contains some information on reliability. If we mark our data as reliable then the data will be resent if the packet is lost during transit. This should only be used for data that is necessary to be received by the other end.
Using the network sender based on the enum type and the packet data class, we can finally send data to the server. However, there is still another distinction to make on how we want to send our data: single or collection data. Single data means that only a single instance of the packet data class will be included in the packet. If we try to send another packet data instance as single data when the packet was not sent yet, it will be overridden. This can be useful if newer instances are strict successors of old data, but if not we can make use of collection data. Collection data is useful if it should be possible to include multiple instances of packet data in one packet. The following code snippet gives an example of both types:
```cs
IClientAddonNetworkSender<ServerPacketId> netSender = ...;
// Send single data using the given packet ID
netSender.SendSingleData(ServerPacketId.PacketId1, new ServerPacketData {
SomeFloat = 3.141592f
});
// Send multiple instance of collection data using another packet ID
for (var someFloat = 1f; someFloat <= 5f; someFloat += 1f) {
netSender.SendCollectionData(ServerPacketId.PacketId2, new ServerPacketData {
SomeFloat = someFloat;
});
}