-
-
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
12 changed files
with
683 additions
and
16 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
39 changes: 39 additions & 0 deletions
39
...sktop/src/main/kotlin/net/rsprot/protocol/game/outgoing/codec/inv/UpdateInvFullEncoder.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,39 @@ | ||
package net.rsprot.protocol.game.outgoing.codec.inv | ||
|
||
import io.netty.channel.ChannelHandlerContext | ||
import net.rsprot.buffer.JagByteBuf | ||
import net.rsprot.protocol.ServerProt | ||
import net.rsprot.protocol.game.outgoing.inv.UpdateInvFull | ||
import net.rsprot.protocol.game.outgoing.prot.GameServerProt | ||
import net.rsprot.protocol.message.codec.MessageEncoder | ||
import net.rsprot.protocol.shared.game.outgoing.inv.InventoryObject | ||
|
||
public class UpdateInvFullEncoder : MessageEncoder<UpdateInvFull> { | ||
override val prot: ServerProt = GameServerProt.UPDATE_INV_FULL | ||
|
||
override fun encode( | ||
ctx: ChannelHandlerContext, | ||
buffer: JagByteBuf, | ||
message: UpdateInvFull, | ||
) { | ||
buffer.p4(message.combinedId.combinedId) | ||
buffer.p2(message.inventoryId) | ||
val capacity = message.capacity | ||
buffer.p2(capacity) | ||
for (i in 0..<capacity) { | ||
val obj = message.getObject(i) | ||
if (obj == InventoryObject.NULL) { | ||
buffer.p1Alt1(0) | ||
buffer.p2Alt3(0) | ||
continue | ||
} | ||
val count = obj.count | ||
buffer.p1Alt1(count.coerceAtMost(0xFF)) | ||
if (count >= 255) { | ||
buffer.p4Alt2(count) | ||
} | ||
buffer.p2Alt3(obj.id + 1) | ||
} | ||
message.returnInventory() | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
...op/src/main/kotlin/net/rsprot/protocol/game/outgoing/codec/inv/UpdateInvPartialEncoder.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,40 @@ | ||
package net.rsprot.protocol.game.outgoing.codec.inv | ||
|
||
import io.netty.channel.ChannelHandlerContext | ||
import net.rsprot.buffer.JagByteBuf | ||
import net.rsprot.protocol.ServerProt | ||
import net.rsprot.protocol.game.outgoing.inv.UpdateInvPartial | ||
import net.rsprot.protocol.game.outgoing.prot.GameServerProt | ||
import net.rsprot.protocol.message.codec.MessageEncoder | ||
import net.rsprot.protocol.metadata.Consistent | ||
import net.rsprot.protocol.shared.game.outgoing.inv.InventoryObject | ||
|
||
@Consistent | ||
public class UpdateInvPartialEncoder : MessageEncoder<UpdateInvPartial> { | ||
override val prot: ServerProt = GameServerProt.UPDATE_INV_PARTIAL | ||
|
||
override fun encode( | ||
ctx: ChannelHandlerContext, | ||
buffer: JagByteBuf, | ||
message: UpdateInvPartial, | ||
) { | ||
buffer.p4(message.combinedId.combinedId) | ||
buffer.p2(message.inventoryId) | ||
val capacity = message.count | ||
for (i in 0..<capacity) { | ||
val obj = message.getObject(i) | ||
buffer.pSmart1or2(obj.slot) | ||
if (obj == InventoryObject.NULL) { | ||
buffer.p2(0) | ||
continue | ||
} | ||
buffer.p2(obj.id + 1) | ||
val count = obj.count | ||
buffer.p1(count.coerceAtMost(0xFF)) | ||
if (count >= 255) { | ||
buffer.p4(count) | ||
} | ||
} | ||
message.returnInventory() | ||
} | ||
} |
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
72 changes: 72 additions & 0 deletions
72
protocol/osrs-221-internal/src/main/kotlin/net/rsprot/protocol/internal/RSProtFlags.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,72 @@ | ||
package net.rsprot.protocol.internal | ||
|
||
import com.github.michaelbull.logging.InlineLogger | ||
import io.netty.util.internal.SystemPropertyUtil | ||
|
||
/** | ||
* An internal object that provides easy access to various error-checking flags. | ||
* The purpose of this object is to avoid scattering these checks throughout | ||
* the codebase, making it difficult for users to find any. | ||
* Additionally, requires duplication of code to re-create all this. | ||
*/ | ||
public object RSProtFlags { | ||
private val logger: InlineLogger = InlineLogger() | ||
private const val PREFIX = "net.rsprot.protocol.internal." | ||
|
||
/** | ||
* Whether the server is in 'development' mode. | ||
* Development mode is effectively a mode where all | ||
* checks are performed to ensure all inputs are validated. | ||
* Users are expected to turn development mode off when | ||
* putting the server into production, as these checks | ||
* end up taking a considerable amount of time. | ||
*/ | ||
private val development: Boolean = | ||
getBoolean( | ||
"development", | ||
true, | ||
) | ||
|
||
/** | ||
* Whether to check that obj ids in inventory packets are all positive. | ||
*/ | ||
public val inventoryObjCheck: Boolean = | ||
getBoolean( | ||
"inventoryObjCheck", | ||
development, | ||
) | ||
|
||
/** | ||
* Whether to validate extended info block inputs. | ||
*/ | ||
public val extendedInfoInputVerification: Boolean = | ||
getBoolean( | ||
"extendedInfoInputVerification", | ||
development, | ||
) | ||
|
||
init { | ||
log("development", development) | ||
log("inventoryObjCheck", inventoryObjCheck) | ||
log("extendedInfoInputVerification", extendedInfoInputVerification) | ||
} | ||
|
||
private fun getBoolean( | ||
propertyName: String, | ||
defaultValue: Boolean, | ||
): Boolean { | ||
return SystemPropertyUtil.getBoolean( | ||
PREFIX + propertyName, | ||
defaultValue, | ||
) | ||
} | ||
|
||
private fun log( | ||
name: String, | ||
value: Boolean, | ||
) { | ||
logger.debug { | ||
"-D${PREFIX}$name: $value" | ||
} | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
...rnal/src/main/kotlin/net/rsprot/protocol/internal/game/outgoing/inv/internal/Inventory.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,63 @@ | ||
package net.rsprot.protocol.internal.game.outgoing.inv.internal | ||
|
||
import net.rsprot.protocol.shared.game.outgoing.inv.InventoryObject | ||
import kotlin.jvm.Throws | ||
|
||
/** | ||
* A compressed internal representation of an inventory, to be transmitted | ||
* with the various inventory update packets. | ||
* Rather than use a List<Obj>, we pool these [Inventory] instances to avoid | ||
* generating significant amounts of garbage. | ||
* For a popular server, it is perfectly reasonable to expect north of a gigabyte | ||
* of memory to be wasted through List<Obj> instances in the span of an hour. | ||
* We eliminate all garbage generation by using soft-reference pooled inventory | ||
* objects. While this does result in a small hit due to the synchronization involved, | ||
* it is nothing compared to the hit caused by garbage collection and memory allocation | ||
* involved with inventories. | ||
* | ||
* @property count the current count of objs in this inventory | ||
* @property contents the array of contents of this inventory. | ||
* The contents array is initialized at the maximum theoretical size | ||
* of the full inv update packet. | ||
*/ | ||
public class Inventory private constructor( | ||
public var count: Int, | ||
private val contents: LongArray, | ||
) { | ||
public constructor( | ||
capacity: Int, | ||
) : this( | ||
0, | ||
LongArray(capacity), | ||
) | ||
|
||
/** | ||
* Adds an obj into this inventory | ||
* @param obj the obj to be added to this inventory | ||
* @throws ArrayIndexOutOfBoundsException if the inventory is full | ||
*/ | ||
@Throws(ArrayIndexOutOfBoundsException::class) | ||
public fun add(obj: InventoryObject) { | ||
contents[count++] = obj.packed | ||
} | ||
|
||
/** | ||
* Gets the obj in [slot]. | ||
* @return the obj in the respective slot, or [InventoryObject.NULL] | ||
* if no object exists in that slot. | ||
* @throws ArrayIndexOutOfBoundsException if the index is out of bounds | ||
*/ | ||
@Throws(ArrayIndexOutOfBoundsException::class) | ||
public operator fun get(slot: Int): InventoryObject { | ||
return InventoryObject(contents[slot]) | ||
} | ||
|
||
/** | ||
* Clears the inventory by setting the count to zero. | ||
* The actual backing long array can remain filled with values, | ||
* as those will be overridden by real usages whenever necessary. | ||
*/ | ||
public fun clear() { | ||
count = 0 | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
.../src/main/kotlin/net/rsprot/protocol/internal/game/outgoing/inv/internal/InventoryPool.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,53 @@ | ||
package net.rsprot.protocol.internal.game.outgoing.inv.internal | ||
|
||
import org.apache.commons.pool2.BasePooledObjectFactory | ||
import org.apache.commons.pool2.ObjectPool | ||
import org.apache.commons.pool2.PooledObject | ||
import org.apache.commons.pool2.PooledObjectFactory | ||
import org.apache.commons.pool2.impl.DefaultPooledObject | ||
import org.apache.commons.pool2.impl.SoftReferenceObjectPool | ||
|
||
/** | ||
* A soft-reference based pool of [Inventory] objects, with the primary | ||
* intent being to avoid re-creating lists of objs which end up wasting | ||
* as much as 137kb of memory for a single inventory that's up to 5713 objs | ||
* in capacity. While it is unlikely that any inventory would get near that, | ||
* servers do commonly expand inventory capacities to numbers like 2,000 or 2,500, | ||
* which would still consume up 48-60kb of memory as a result in any traditional manner. | ||
* | ||
* Breakdown of the above statements: | ||
* Assuming an implementation where List<Obj> is provided to the respective packets, | ||
* where Obj is a class of three properties: | ||
* | ||
* ``` | ||
* Slot: Int (necessary for partial inv updates) | ||
* Id: Int | ||
* Count: Int | ||
* ``` | ||
* | ||
* The resulting memory requirement would be `(12 + (3 * 4))` bytes per obj. | ||
* While this does coincide with the memory alignment, | ||
* it still ends up consuming 24 bytes per obj, all of which would be discarded shortly after. | ||
* Given the assumption that 1,000 players log in at once, and they all have a bank | ||
* of 1000 objs - which is a fairly conservative estimate -, the resulting waste of memory | ||
* is 50 megabytes alone. All of this can be avoided through the use of an object pool, | ||
* as done below. | ||
*/ | ||
public data object InventoryPool { | ||
public val pool: ObjectPool<Inventory> = SoftReferenceObjectPool(createFactory()) | ||
|
||
private fun createFactory(): PooledObjectFactory<Inventory> { | ||
return object : BasePooledObjectFactory<Inventory>() { | ||
override fun create(): Inventory { | ||
// 5713 is the maximum theoretical number of objs an inventory can carry | ||
// before the 40kb limitation could get hit | ||
// This assumes each obj sends a quantity of >= 255 | ||
return Inventory(5713) | ||
} | ||
|
||
override fun wrap(p0: Inventory): PooledObject<Inventory> { | ||
return DefaultPooledObject(p0) | ||
} | ||
} | ||
} | ||
} |
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
Oops, something went wrong.