-
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: revision 228 (initial copy of 227) * refactor: update game server prot ids * refactor: update game client prot ids * feat: client prots * feat: all but info packets * feat: player info packet * feat: npc info packet * feat: login crc * fix: revision 228 tests
- Loading branch information
Showing
741 changed files
with
53,246 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
// No-op |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
dependencies { | ||
implementation(platform(rootProject.libs.netty.bom)) | ||
api(rootProject.libs.netty.buffer) | ||
implementation(rootProject.libs.netty.transport) | ||
implementation(rootProject.libs.netty.handler) | ||
implementation(rootProject.libs.netty.native.epoll) | ||
implementation(rootProject.libs.netty.native.kqueue) | ||
implementation(rootProject.libs.netty.incubator.iouring) | ||
implementation(rootProject.libs.netty.native.macos.dns.resolver) | ||
val epollClassifiers = listOf("linux-aarch_64", "linux-x86_64", "linux-riscv64") | ||
val kqueueClassifiers = listOf("osx-x86_64") | ||
val iouringClassifiers = listOf("linux-aarch_64", "linux-x86_64") | ||
for (classifier in epollClassifiers) { | ||
implementation(variantOf(rootProject.libs.netty.native.epoll) { classifier(classifier) }) | ||
} | ||
for (classifier in kqueueClassifiers) { | ||
implementation(variantOf(rootProject.libs.netty.native.kqueue) { classifier(classifier) }) | ||
} | ||
for (classifier in iouringClassifiers) { | ||
implementation(variantOf(rootProject.libs.netty.incubator.iouring) { classifier(classifier) }) | ||
} | ||
implementation(rootProject.libs.inline.logger) | ||
api(projects.protocol) | ||
api(projects.compression) | ||
api(projects.crypto) | ||
api(projects.protocol.osrs228.osrs228Common) | ||
api(projects.protocol.osrs228.osrs228Model) | ||
implementation(projects.protocol.osrs228.osrs228Internal) | ||
implementation(projects.protocol.osrs228.osrs228Desktop) | ||
implementation(projects.protocol.osrs228.osrs228Shared) | ||
implementation(projects.buffer) | ||
} | ||
|
||
mavenPublishing { | ||
pom { | ||
name = "RsProt OSRS 228 API" | ||
description = "The API module for revision 228 OldSchool RuneScape networking, " + | ||
"offering an all-in-one implementation." | ||
} | ||
} |
393 changes: 393 additions & 0 deletions
393
...228/osrs-228-api/src/main/kotlin/net/rsprot/protocol/api/AbstractNetworkServiceFactory.kt
Large diffs are not rendered by default.
Oops, something went wrong.
20 changes: 20 additions & 0 deletions
20
.../osrs-228/osrs-228-api/src/main/kotlin/net/rsprot/protocol/api/ChannelExceptionHandler.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package net.rsprot.protocol.api | ||
|
||
import io.netty.channel.ChannelHandlerContext | ||
|
||
/** | ||
* The channel exception handler is an interface that is invoked whenever Netty catches | ||
* an exception in the channels. The server is expected to close the connection in any such case, | ||
* if it is still open, and log the exception behind it. | ||
*/ | ||
public fun interface ChannelExceptionHandler { | ||
/** | ||
* Invoked whenever a Netty handler catches an exception. | ||
* @param ctx the channel handler context behind this connection | ||
* @param cause the causation behind the exception | ||
*/ | ||
public fun exceptionCaught( | ||
ctx: ChannelHandlerContext, | ||
cause: Throwable, | ||
) | ||
} |
227 changes: 227 additions & 0 deletions
227
...ocol/osrs-228/osrs-228-api/src/main/kotlin/net/rsprot/protocol/api/EntityInfoProtocols.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
package net.rsprot.protocol.api | ||
|
||
import io.netty.buffer.ByteBufAllocator | ||
import net.rsprot.compression.provider.HuffmanCodecProvider | ||
import net.rsprot.protocol.api.suppliers.NpcInfoSupplier | ||
import net.rsprot.protocol.api.suppliers.PlayerInfoSupplier | ||
import net.rsprot.protocol.api.suppliers.WorldEntityInfoSupplier | ||
import net.rsprot.protocol.common.client.ClientTypeMap | ||
import net.rsprot.protocol.common.client.OldSchoolClientType | ||
import net.rsprot.protocol.common.game.outgoing.info.npcinfo.encoder.NpcResolutionChangeEncoder | ||
import net.rsprot.protocol.common.game.outgoing.info.util.ZoneIndexStorage | ||
import net.rsprot.protocol.game.outgoing.codec.npcinfo.DesktopLowResolutionChangeEncoder | ||
import net.rsprot.protocol.game.outgoing.codec.npcinfo.extendedinfo.writer.NpcAvatarExtendedInfoDesktopWriter | ||
import net.rsprot.protocol.game.outgoing.codec.playerinfo.extendedinfo.writer.PlayerAvatarExtendedInfoDesktopWriter | ||
import net.rsprot.protocol.game.outgoing.info.npcinfo.DeferredNpcInfoProtocolSupplier | ||
import net.rsprot.protocol.game.outgoing.info.npcinfo.NpcAvatarExtendedInfoWriter | ||
import net.rsprot.protocol.game.outgoing.info.npcinfo.NpcAvatarFactory | ||
import net.rsprot.protocol.game.outgoing.info.npcinfo.NpcInfoProtocol | ||
import net.rsprot.protocol.game.outgoing.info.playerinfo.PlayerAvatarExtendedInfoWriter | ||
import net.rsprot.protocol.game.outgoing.info.playerinfo.PlayerAvatarFactory | ||
import net.rsprot.protocol.game.outgoing.info.playerinfo.PlayerInfoProtocol | ||
import net.rsprot.protocol.game.outgoing.info.worldentityinfo.WorldEntityAvatarFactory | ||
import net.rsprot.protocol.game.outgoing.info.worldentityinfo.WorldEntityProtocol | ||
|
||
/** | ||
* The entity info protocols class brings together the relatively complex player and NPC info | ||
* protocols. This is responsible for registering all the client types that are used by the user. | ||
* @property playerAvatarFactory the avatar factory for players. Since players have a 1:1 player info | ||
* to avatar ratio, the avatar is automatically included in the player info object that is requested. | ||
* This is additionally strategically placed to improve cache locality and improve the performance | ||
* of the player info protocol. | ||
* @property playerInfoProtocol the main player info protocol responsible for computing the player | ||
* info packet for all the players in the game. | ||
* @property npcAvatarFactory the avatar factory for NPCs. Each NPC must allocate one avatar | ||
* as they spawn, and that avatar must be deallocated when the NPC is fully removed from the game. | ||
* @property npcInfoProtocol the main NPC info protocol responsible for computing the npc info packet | ||
* for all the players in the game. | ||
*/ | ||
public class EntityInfoProtocols | ||
private constructor( | ||
public val playerAvatarFactory: PlayerAvatarFactory, | ||
public val playerInfoProtocol: PlayerInfoProtocol, | ||
public val npcAvatarFactory: NpcAvatarFactory, | ||
public val npcInfoProtocol: NpcInfoProtocol, | ||
public val worldEntityAvatarFactory: WorldEntityAvatarFactory, | ||
public val worldEntityInfoProtocol: WorldEntityProtocol, | ||
) { | ||
internal companion object { | ||
/** | ||
* Initializes the player and NPC info avatar factories and protocols. | ||
* @param allocator the byte buffer allocator used for player and NPC info main buffers, | ||
* as well as any pre-computed extended info blocks. | ||
* @param clientTypes the list of client types to register | ||
* @param huffmanCodecProvider the Huffman codec provider that will be used to compute | ||
* the chat extended info block, any others in the future that may require it. | ||
* @param playerInfoSupplier the class wrapping the worker used to perform computations, | ||
* as well as the filter for extended info blocks that ensures that the packet does not | ||
* under any circumstances exceed the maximum packet limitations. | ||
* @param npcInfoSupplier the class wrapping the worker used to perform computations, | ||
* as well as the filter for extended info blocks that ensures that the packet does not | ||
* under any circumstances exceed the maximum packet limitations. Furthermore, unlike | ||
* player info, this will also provide an implementation for exceptions caught during | ||
* pre-computations of NPC avatars. It is up to the server to decide how to handle | ||
* any exceptions which are caught when computing information for avatars. The least | ||
* destructive way is to remove the underlying NPC from the world when that happens, | ||
* and log the exception in the process. This will still cause any observers to disconnect, | ||
* however, but it ensures that anyone else that comes around the same area will not | ||
* experience the same fate. There is also an implementation that is used to supply | ||
* indices of nearby NPCs for the NPC info packet from the server's perspective, | ||
* given a number of arguments necessary to determine it. The server is expected | ||
* to return an iterator of all the indices of the NPCs that match the predicate, | ||
* even if a NPC is already tracked by a given player. The protocol is responsible | ||
* for ensuring no duplications will occur. | ||
* @return a class wrapping all the protocols into one object. | ||
*/ | ||
fun initialize( | ||
allocator: ByteBufAllocator, | ||
clientTypes: List<OldSchoolClientType>, | ||
huffmanCodecProvider: HuffmanCodecProvider, | ||
playerInfoSupplier: PlayerInfoSupplier, | ||
npcInfoSupplier: NpcInfoSupplier, | ||
worldEntityInfoSupplier: WorldEntityInfoSupplier, | ||
): EntityInfoProtocols { | ||
val playerWriters = mutableListOf<PlayerAvatarExtendedInfoWriter>() | ||
val npcWriters = mutableListOf<NpcAvatarExtendedInfoWriter>() | ||
val npcResolutionChangeEncoders = mutableListOf<NpcResolutionChangeEncoder>() | ||
if (OldSchoolClientType.DESKTOP in clientTypes) { | ||
playerWriters += PlayerAvatarExtendedInfoDesktopWriter() | ||
npcWriters += NpcAvatarExtendedInfoDesktopWriter() | ||
npcResolutionChangeEncoders += DesktopLowResolutionChangeEncoder() | ||
} | ||
val zoneIndexStorage = ZoneIndexStorage(ZoneIndexStorage.WORLDENTITY_CAPACITY) | ||
val worldEntityAvatarFactory = | ||
buildWorldEntityAvatarFactory( | ||
allocator, | ||
zoneIndexStorage, | ||
) | ||
val worldEntityProtocol = | ||
buildWorldEntityInfoProtocol( | ||
allocator, | ||
worldEntityInfoSupplier, | ||
worldEntityAvatarFactory, | ||
zoneIndexStorage, | ||
) | ||
val playerAvatarFactory = | ||
buildPlayerAvatarFactory(allocator, playerInfoSupplier, playerWriters, huffmanCodecProvider) | ||
val playerInfoProtocol = | ||
buildPlayerInfoProtocol( | ||
allocator, | ||
playerInfoSupplier, | ||
playerAvatarFactory, | ||
) | ||
val storage = ZoneIndexStorage(ZoneIndexStorage.NPC_CAPACITY) | ||
val supplier = DeferredNpcInfoProtocolSupplier() | ||
val npcAvatarFactory = | ||
buildNpcAvatarFactory( | ||
allocator, | ||
npcInfoSupplier, | ||
npcWriters, | ||
huffmanCodecProvider, | ||
storage, | ||
supplier, | ||
) | ||
val npcInfoProtocol = | ||
buildNpcInfoProtocol( | ||
allocator, | ||
npcInfoSupplier, | ||
npcResolutionChangeEncoders, | ||
npcAvatarFactory, | ||
storage, | ||
) | ||
supplier.supply(npcInfoProtocol) | ||
|
||
return EntityInfoProtocols( | ||
playerAvatarFactory, | ||
playerInfoProtocol, | ||
npcAvatarFactory, | ||
npcInfoProtocol, | ||
worldEntityAvatarFactory, | ||
worldEntityProtocol, | ||
) | ||
} | ||
|
||
private fun buildNpcInfoProtocol( | ||
allocator: ByteBufAllocator, | ||
npcInfoSupplier: NpcInfoSupplier, | ||
npcResolutionChangeEncoders: MutableList<NpcResolutionChangeEncoder>, | ||
npcAvatarFactory: NpcAvatarFactory, | ||
zoneIndexStorage: ZoneIndexStorage, | ||
) = NpcInfoProtocol( | ||
allocator, | ||
ClientTypeMap.of( | ||
npcResolutionChangeEncoders, | ||
OldSchoolClientType.COUNT, | ||
) { | ||
it.clientType | ||
}, | ||
npcAvatarFactory, | ||
npcInfoSupplier.npcAvatarExceptionHandler, | ||
npcInfoSupplier.npcInfoProtocolWorker, | ||
zoneIndexStorage, | ||
) | ||
|
||
private fun buildNpcAvatarFactory( | ||
allocator: ByteBufAllocator, | ||
npcInfoSupplier: NpcInfoSupplier, | ||
npcWriters: MutableList<NpcAvatarExtendedInfoWriter>, | ||
huffmanCodecProvider: HuffmanCodecProvider, | ||
zoneIndexStorage: ZoneIndexStorage, | ||
npcInfoProtocolSupplier: DeferredNpcInfoProtocolSupplier, | ||
): NpcAvatarFactory = | ||
NpcAvatarFactory( | ||
allocator, | ||
npcInfoSupplier.npcExtendedInfoFilter, | ||
npcWriters, | ||
huffmanCodecProvider, | ||
zoneIndexStorage, | ||
npcInfoProtocolSupplier, | ||
) | ||
|
||
private fun buildWorldEntityAvatarFactory( | ||
allocator: ByteBufAllocator, | ||
zoneIndexStorage: ZoneIndexStorage, | ||
): WorldEntityAvatarFactory = | ||
WorldEntityAvatarFactory( | ||
allocator, | ||
zoneIndexStorage, | ||
) | ||
|
||
private fun buildWorldEntityInfoProtocol( | ||
allocator: ByteBufAllocator, | ||
worldEntityInfoSupplier: WorldEntityInfoSupplier, | ||
worldEntityAvatarFactory: WorldEntityAvatarFactory, | ||
zoneIndexStorage: ZoneIndexStorage, | ||
) = WorldEntityProtocol( | ||
allocator, | ||
worldEntityInfoSupplier.worldEntityAvatarExceptionHandler, | ||
worldEntityAvatarFactory, | ||
worldEntityInfoSupplier.worldEntityInfoProtocolWorker, | ||
zoneIndexStorage, | ||
) | ||
|
||
private fun buildPlayerInfoProtocol( | ||
allocator: ByteBufAllocator, | ||
playerInfoSupplier: PlayerInfoSupplier, | ||
playerAvatarFactory: PlayerAvatarFactory, | ||
): PlayerInfoProtocol = | ||
PlayerInfoProtocol( | ||
allocator, | ||
playerInfoSupplier.playerInfoProtocolWorker, | ||
playerAvatarFactory, | ||
) | ||
|
||
private fun buildPlayerAvatarFactory( | ||
allocator: ByteBufAllocator, | ||
playerInfoSupplier: PlayerInfoSupplier, | ||
playerWriters: MutableList<PlayerAvatarExtendedInfoWriter>, | ||
huffmanCodecProvider: HuffmanCodecProvider, | ||
): PlayerAvatarFactory = | ||
PlayerAvatarFactory( | ||
allocator, | ||
playerInfoSupplier.playerExtendedInfoFilter, | ||
playerWriters, | ||
huffmanCodecProvider, | ||
) | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
...ol/osrs-228/osrs-228-api/src/main/kotlin/net/rsprot/protocol/api/GameConnectionHandler.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package net.rsprot.protocol.api | ||
|
||
import net.rsprot.crypto.xtea.XteaKey | ||
import net.rsprot.protocol.api.login.GameLoginResponseHandler | ||
import net.rsprot.protocol.loginprot.incoming.util.AuthenticationType | ||
import net.rsprot.protocol.loginprot.incoming.util.LoginBlock | ||
|
||
/** | ||
* A handler interface for any game logins and reconnections. | ||
* @param R the receiver of the incoming game packets, typically a Player class. | ||
*/ | ||
public interface GameConnectionHandler<R> { | ||
/** | ||
* The onLogin function is triggered whenever a login request is received by the library, | ||
* and it passes all the initial validation necessary. The server is responsible | ||
* for doing most of the validation here, but preliminary things like max number of connections | ||
* and session ids will have been pre-checked by us. | ||
* @param responseHandler the handler used to write a successful or failed login response, | ||
* depending on the decisions made by the server. | ||
* @param block the login block sent by the client, containing all the information the server | ||
* will need. | ||
*/ | ||
public fun onLogin( | ||
responseHandler: GameLoginResponseHandler<R>, | ||
block: LoginBlock<AuthenticationType<*>>, | ||
) | ||
|
||
/** | ||
* The onReconnect function is triggered whenever a reconnect request is received | ||
* by the library. It is worth noting that Proof of Work will not be involved | ||
* if this is the case, assuming it is enabled in the first place. | ||
* Instead of transmitting the password, the client will transmit the seed used | ||
* by the previous login connection. If the seed does not match with what the | ||
* server knows, the request should be rejected. If the reconnect is successful, | ||
* the server should replace the Session object in that player with the one | ||
* provided by the response handler. The old session will close or time out shortly | ||
* afterwards, if it already hasn't. | ||
* @param responseHandler the handler used to write a successful or failed reconnect response, | ||
* depending on the decisions made by the server. | ||
* @param block the login block sent by the client, containing all the information the server | ||
* will need. | ||
*/ | ||
public fun onReconnect( | ||
responseHandler: GameLoginResponseHandler<R>, | ||
block: LoginBlock<XteaKey>, | ||
) | ||
} |
32 changes: 32 additions & 0 deletions
32
protocol/osrs-228/osrs-228-api/src/main/kotlin/net/rsprot/protocol/api/GameMessageCounter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package net.rsprot.protocol.api | ||
|
||
import net.rsprot.protocol.ClientProtCategory | ||
|
||
/** | ||
* An interface for tracking incoming game messages, in order to avoid | ||
* decoding and consuming too many messages if the client is flooding us | ||
* with them. | ||
* This implementation must be thread safe in the sense that the | ||
* increment and reset functions could be called concurrently from different | ||
* threads. The default implementation uses an array for tracking the counts | ||
* and thus does not need such thread safety here. | ||
*/ | ||
public interface GameMessageCounter { | ||
/** | ||
* Increments the message counter for the provided client prot | ||
* category. | ||
* @param clientProtCategory the category of the incoming packet. | ||
*/ | ||
public fun increment(clientProtCategory: ClientProtCategory) | ||
|
||
/** | ||
* Whether any of the message categories have reached their limit | ||
* for maximum number of decoded messages. | ||
*/ | ||
public fun isFull(): Boolean | ||
|
||
/** | ||
* Resets the tracked counts for the messages. | ||
*/ | ||
public fun reset() | ||
} |
15 changes: 15 additions & 0 deletions
15
...rs-228/osrs-228-api/src/main/kotlin/net/rsprot/protocol/api/GameMessageCounterProvider.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package net.rsprot.protocol.api | ||
|
||
/** | ||
* Gets the message counter provider for incoming game messages. | ||
* This is in a provider implementation as one instance is allocated | ||
* for each session object. | ||
*/ | ||
public fun interface GameMessageCounterProvider { | ||
/** | ||
* Provides a game message counter implementation. | ||
* A new instance must be allocated with each request, | ||
* as this is per session basis. | ||
*/ | ||
public fun provide(): GameMessageCounter | ||
} |
Oops, something went wrong.