Version: 2.0.0

Wrapping Custom Player Objects

How one might do it#

A common pattern when using remotes - on the server - is to require casting a player to an object representation of the player.

This may come as the following sort of situation:

import PlayerService from "server/Services/PlayerService";
const PlayerEquipItem = new Net.Server.Event<[itemId: number]>("PlayerEquipItem")
PlayerEquipItem.Connect((player, itemId) => {
const entity = PlayerService.GetEntity(player);
if (entity) {
entity.EquipItem(itemId);
}
})
const PlayerUnequipItem = new Net.Server.Event<[itemId: number]>("PlayerUnequipItem")
PlayerUnequipItem.Connect((player, itemId) => {
const entity = PlayerService.GetEntity(player);
if (entity) {
entity.UnequipItem(itemId);
}
})
const GetPlayerEquipped = new Net.Server.AsyncFunction("GetPlayerEquipped")
GetPlayerEquipped.SetCallback((player) => {
const entity = PlayerService.GetEntity(player);
if (entity) {
return entity.GetEquippedItems();
}
})

As you might have noticed, there's some repeated code where we're repetitively having to grab the player entity here in place of the player.

We can instead use what's called a wrapper to do this for us and make it as if we were connecting directly to the remote and it had the entity as the player argument rather than a player.

Using a wrapper#

We can define a withPlayerEntity wrapper, which will achieve what we're doing above in each remote separately. This is the recommended method when augmenting the player argument as it's the safest method and guaranteed to run after all the middleware.

server/Wrappers/withPlayerEntity.ts
import PlayerService from "server/Services/PlayerService";
import PlayerEntity from "server/Classes/PlayerEntity";
export default function withPlayerEntity<T extends Array<unknown>, R = void>(
fn: (playerEntity: PlayerEntity, ...args: T) => R,
) {
return (player: Player, ...args: T) => {
const entity = PlayerService.GetEntity(player);
if (entity) {
return fn(entity, ...args);
}
};
}

Then we can apply this to the code above.

import PlayerService from "server/Services/PlayerService";
import withPlayerEntity from "server/Wrappers/withPlayerEntity";
const PlayerEquipItem = new Net.Server.Event<[itemId: number]>("PlayerEquipItem")
PlayerEquipItem.Connect(
withPlayerEntity((entity, itemId) => {
entity.EquipItem(itemId);
})
);
const PlayerUnequipItem = new Net.Server.Event<[itemId: number]>("PlayerUnequipItem")
PlayerUnequipItem.Connect((player, itemId) => {
withPlayerEntity((entity, itemId) => {
entity.UnequipItem(itemId);
})
})
const GetPlayerEquipped = new Net.Server.AsyncFunction("GetPlayerEquipped")
GetPlayerEquipped.SetCallback(
withPlayerEntity((player) => {
return entity.GetEquippedItems();
})
)

And as you can see, this reduces repetitive code and gives us the PlayerEntity object to work with. Simple as that.