-
-
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.
- Loading branch information
Showing
10 changed files
with
497 additions
and
18 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,46 @@ | ||
package net.rsprot.crypto.util | ||
|
||
public class XteaKey( | ||
public val key: IntArray, | ||
) { | ||
public constructor( | ||
key1: Int, | ||
key2: Int, | ||
key3: Int, | ||
key4: Int, | ||
) : this( | ||
intArrayOf( | ||
key1, | ||
key2, | ||
key3, | ||
key4, | ||
), | ||
) | ||
|
||
init { | ||
require(key.size == 4) { | ||
"Xtea keys must be 128 bits in length (4 integers)" | ||
} | ||
} | ||
|
||
override fun equals(other: Any?): Boolean { | ||
if (this === other) return true | ||
if (javaClass != other?.javaClass) return false | ||
|
||
other as XteaKey | ||
|
||
return key.contentEquals(other.key) | ||
} | ||
|
||
override fun hashCode(): Int { | ||
return key.contentHashCode() | ||
} | ||
|
||
override fun toString(): String { | ||
return "XteaKey(key=${key.contentToString()})" | ||
} | ||
|
||
public companion object { | ||
public val ZERO: XteaKey = XteaKey(0, 0, 0, 0) | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
...sktop/src/main/kotlin/net/rsprot/protocol/game/outgoing/codec/map/RebuildNormalEncoder.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,29 @@ | ||
package net.rsprot.protocol.game.outgoing.codec.map | ||
|
||
import io.netty.channel.ChannelHandlerContext | ||
import net.rsprot.buffer.JagByteBuf | ||
import net.rsprot.protocol.ServerProt | ||
import net.rsprot.protocol.game.outgoing.map.RebuildNormal | ||
import net.rsprot.protocol.game.outgoing.prot.GameServerProt | ||
import net.rsprot.protocol.message.codec.MessageEncoder | ||
|
||
public class RebuildNormalEncoder : MessageEncoder<RebuildNormal> { | ||
override val prot: ServerProt = GameServerProt.REBUILD_NORMAL | ||
|
||
override fun encode( | ||
ctx: ChannelHandlerContext, | ||
buffer: JagByteBuf, | ||
message: RebuildNormal, | ||
) { | ||
buffer.p2Alt3(message.zoneX) | ||
buffer.p2Alt3(message.zoneZ) | ||
// Currently unused property, unknown what it is for, presumably sailing-related | ||
buffer.p2Alt2(0) | ||
buffer.p2(message.keys.size) | ||
for (xteaKey in message.keys) { | ||
for (intKey in xteaKey.key) { | ||
buffer.p4(intKey) | ||
} | ||
} | ||
} | ||
} |
110 changes: 110 additions & 0 deletions
110
...sktop/src/main/kotlin/net/rsprot/protocol/game/outgoing/codec/map/RebuildRegionEncoder.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,110 @@ | ||
package net.rsprot.protocol.game.outgoing.codec.map | ||
|
||
import io.netty.channel.ChannelHandlerContext | ||
import net.rsprot.buffer.JagByteBuf | ||
import net.rsprot.buffer.bitbuffer.toBitBuf | ||
import net.rsprot.crypto.util.XteaKey | ||
import net.rsprot.protocol.ServerProt | ||
import net.rsprot.protocol.game.outgoing.map.RebuildRegion | ||
import net.rsprot.protocol.game.outgoing.prot.GameServerProt | ||
import net.rsprot.protocol.message.codec.MessageEncoder | ||
|
||
public class RebuildRegionEncoder : MessageEncoder<RebuildRegion> { | ||
override val prot: ServerProt = GameServerProt.REBUILD_REGION | ||
|
||
override fun encode( | ||
ctx: ChannelHandlerContext, | ||
buffer: JagByteBuf, | ||
message: RebuildRegion, | ||
) { | ||
buffer.p2(message.zoneX) | ||
buffer.p2Alt2(message.zoneZ) | ||
buffer.p1Alt2(if (message.reload) 1 else 0) | ||
|
||
// Xtea count, temporary value | ||
val marker = buffer.writerIndex() | ||
buffer.p2(0) | ||
|
||
var xteaCount = 0 | ||
val (mapsquares, xteas) = distinctMapsquares.get() | ||
val bitbuf = buffer.buffer.toBitBuf() | ||
bitbuf.use { | ||
for (zone in message.zones) { | ||
if (zone == null) { | ||
bitbuf.pBits(1, 0) | ||
continue | ||
} | ||
bitbuf.pBits(1, 1) | ||
bitbuf.pBits(26, zone.referenceZone.packed) | ||
val mapsquareId = zone.referenceZone.mapsquareId | ||
if (contains(mapsquares, xteaCount, mapsquareId)) { | ||
continue | ||
} | ||
mapsquares[xteaCount] = mapsquareId | ||
xteas[xteaCount] = zone.key | ||
xteaCount++ | ||
} | ||
} | ||
// Write the real xtea count | ||
val writerIndex = buffer.writerIndex() | ||
buffer.writerIndex(marker) | ||
buffer.p2(xteaCount) | ||
buffer.writerIndex(writerIndex) | ||
|
||
for (i in 0..<xteaCount) { | ||
val xteaKey = xteas[i] | ||
for (intKey in xteaKey.key) { | ||
buffer.p4(intKey) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Check if the [array] contains the [value] in it, up until [length] (exclusive). | ||
* As our arrays are pre-initialized to a capacity of 676, we do not want to search | ||
* the entire thing when we have only added a few elements to it. | ||
* Additionally, since we do not zero out the arrays, anything beyond the [length] | ||
* would be phantom data from previous packets. | ||
* @param array the int array to search | ||
* @param length the length of the array that has been filled up | ||
* @param value the value to seek for | ||
* @return whether the int array contains the [value] in the first [length] indices | ||
*/ | ||
private fun contains( | ||
array: IntArray, | ||
length: Int, | ||
value: Int, | ||
): Boolean { | ||
for (i in 0..<length) { | ||
val element = array[i] | ||
if (element == value) { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
private companion object { | ||
/** | ||
* The maximum theoretical number of mapsquares that can be sent in a single | ||
* rebuild region packet. | ||
*/ | ||
private const val MAX_POTENTIAL_MAPSQUARES = 4 * 13 * 13 | ||
|
||
/** | ||
* A thread-local implementation of mapsquares and their keys. | ||
* As we need to trim our data set down to distinct mapsquares, | ||
* doing so with new lists all the time can be quite wasteful, especially | ||
* knowing how volatile the actual counts can be. | ||
* To minimize the garbage created (in this case, to none), | ||
* we use thread-local implementations for distinct mapsquares. | ||
*/ | ||
private val distinctMapsquares = | ||
ThreadLocal.withInitial { | ||
IntArray(MAX_POTENTIAL_MAPSQUARES) to | ||
Array(4 * 13 * 13) { | ||
XteaKey.ZERO | ||
} | ||
} | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
...ktop/src/main/kotlin/net/rsprot/protocol/loginprot/incoming/codec/GameReconnectDecoder.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
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
85 changes: 85 additions & 0 deletions
85
...col/osrs-221-model/src/main/kotlin/net/rsprot/protocol/game/outgoing/map/RebuildNormal.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,85 @@ | ||
package net.rsprot.protocol.game.outgoing.map | ||
|
||
import net.rsprot.crypto.util.XteaKey | ||
import net.rsprot.protocol.game.outgoing.map.util.XteaProvider | ||
import net.rsprot.protocol.message.OutgoingMessage | ||
|
||
/** | ||
* Rebuild normal is sent when the game requires a map reload without being in instances. | ||
* @property zoneX the x coordinate of the local player's current zone. | ||
* @property zoneZ the z coordinate of the local player's current zone. | ||
* @property keys the list of xtea keys needed to decrypt the map. | ||
*/ | ||
public class RebuildNormal private constructor( | ||
private val _zoneX: UShort, | ||
private val _zoneZ: UShort, | ||
public val keys: List<XteaKey>, | ||
) : OutgoingMessage { | ||
public constructor( | ||
zoneX: Int, | ||
zoneZ: Int, | ||
keyProvider: XteaProvider, | ||
) : this( | ||
zoneX.toUShort(), | ||
zoneZ.toUShort(), | ||
buildXteaKeyList(zoneX, zoneZ, keyProvider), | ||
) | ||
|
||
public val zoneX: Int | ||
get() = _zoneX.toInt() | ||
public val zoneZ: Int | ||
get() = _zoneZ.toInt() | ||
|
||
override fun equals(other: Any?): Boolean { | ||
if (this === other) return true | ||
if (javaClass != other?.javaClass) return false | ||
|
||
other as RebuildNormal | ||
|
||
if (_zoneX != other._zoneX) return false | ||
if (_zoneZ != other._zoneZ) return false | ||
if (keys != other.keys) return false | ||
|
||
return true | ||
} | ||
|
||
override fun hashCode(): Int { | ||
var result = _zoneX.hashCode() | ||
result = 31 * result + _zoneZ.hashCode() | ||
result = 31 * result + keys.hashCode() | ||
return result | ||
} | ||
|
||
override fun toString(): String { | ||
return "RebuildNormal(" + | ||
"zoneX=$zoneX, " + | ||
"zoneZ=$zoneZ, " + | ||
"keys=$keys" + | ||
")" | ||
} | ||
|
||
private companion object { | ||
/** | ||
* A helper function to build the mapsquare key list the same way the client does, | ||
* as the keys must be in the same specific order as the client reads it. | ||
*/ | ||
private fun buildXteaKeyList( | ||
zoneX: Int, | ||
zoneZ: Int, | ||
keyProvider: XteaProvider, | ||
): List<XteaKey> { | ||
val minMapsquareX = (zoneX - 6) ushr 3 | ||
val maxMapsquareX = (zoneX + 6) ushr 3 | ||
val minMapsquareZ = (zoneZ - 6) ushr 3 | ||
val maxMapsquareZ = (zoneZ + 6) ushr 3 | ||
val count = (maxMapsquareX - minMapsquareZ + 1) * (maxMapsquareZ - minMapsquareZ + 1) | ||
val keys = ArrayList<XteaKey>(count) | ||
for (mapsquareX in minMapsquareX..maxMapsquareX) { | ||
for (mapsquareZ in minMapsquareZ..maxMapsquareZ) { | ||
keys += keyProvider.provide((mapsquareX shl 8) or mapsquareZ) | ||
} | ||
} | ||
return keys | ||
} | ||
} | ||
} |
Oops, something went wrong.