Essential Session Management¶
Due to Cyberpunk being a single player game, certain Systems behave in ways that are not suitable for multi-player environments. The BlackICE framework handles countless cases of that in itself, but certain adjustments are left to the server owners choice.
Below you'll find examples of special cases every server should handle. This list is expected to grow over time, once the community discovers new quirks and ways to deal with them.
Our example bundles implement most of these, if you are looking for live examples
Found something that is not referenced here?¶
If you stumbled across another bug and managed to fix it by hooking or modifying state, feel free to post in the forums or discord about it. We gladely add it to this documentation for future reference.
Fixes¶
Lifecycle¶
The usual lifecycle of the game in single player ends when the player puppet dies. This raises the DeathMenu and usually leads o
reloading the last save. In MP, you likely want to implement custom respawning logic.
This example adds a client sided "revive" command, together with strategically placed hooks that prevent the game from advancing into
the DeathMenu state.
You can modify this in countless ways. Add timers, conditions or revive via a net event
public static class SessionManager
{
public static void Start()
{
bool revive = false;
re.Con.RegisterCommand(
"revive",
(string[] args) =>
{
revive = true;
}
);
re.Hooks.Place(
"DeathDecisionsWithResurrection",
"ToResurrect;StateContextStateGameScriptInterface",
(DeathDecisionsWithResurrection_ToResurrect x) =>
{
if (revive)
{
revive = false;
return new re.msgpack.Bool(true);
}
return new re.msgpack.Bool(false);
}
);
re.Hooks.PlaceVoid(
"PlayerGameplayRestrictions",
"PlayerGameplayRestrictions::SendBlockMenuRequest;PlayerPuppetBoolEMenuType",
(PlayerGameplayRestrictions_SendBlockMenuRequest x) => { }
);
re.Hooks.Place(
"HighLevelTransition",
"IsDeathMenuBlocked;StateGameScriptInterface",
(HighLevelTransition_IsDeathMenuBlocked x) =>
{
return new re.msgpack.Bool(true);
}
);
}
}
[Msgpack.Gen]
public readonly partial record struct DeathDecisionsWithResurrection_ToResurrect(
DeathDecisionsWithResurrection self,
game.stateMachine.StateContextScript? stateContext,
game.stateMachine.GameScriptInterface? scriptInterface
);
[Msgpack.Gen]
public readonly partial record struct PlayerGameplayRestrictions_SendBlockMenuRequest(
WeakHandle<PlayerPuppet> player,
bool blockMenu,
EMenuType menuType
);
[Msgpack.Gen]
public readonly partial record struct HighLevelTransition_IsDeathMenuBlocked(
HighLevelTransition self,
game.stateMachine.GameScriptInterface? scriptInterface
);
Puppet customization¶
The games player management system does not expect multiple PlayerPuppets to be created within one session.
If a server changes your observer entity to a fresh puppet, the PlayerDevelopmentSystem thinks it already
equipped all the necessary items on your previous one. Hooking this function and removing the flag will cause
the system to equip the new PlayerPuppet with all the default items again. You can then apply further customizations
as you wish.
re.Hooks.PlaceVoid(
"PlayerDevelopmentSystem",
"OnPlayerAttach;PlayerAttachRequest",
(UIInventoryScriptableSystem_OnPlayerAttach x) =>
{
re.Hooks.CallOriginalVoid();
x.self.playerDevelopmentUpdated = false;
}
);
Puppet damage FX¶
If a remote PlayerPuppet in your proxmity falls below a certain health threshold, you will get screen FX from that. This is because the scripts do not check if a PlayerPuppet is your local one, but attach StatPool listeners uncoditionally. Your client believes the remote puppets health is your's and renders damage FX overlays. To fix this, hook the following to prevent all PlayerPuppet init code of other puppets to run on your client:
re.Hooks.Place(
"PlayerPuppet",
"OnGameAttached",
(PlayerPuppet_OnGameAttached x) =>
{
if (!x.self.IsControlledByLocalPeer())
{
return new re.msgpack.Bool(true);
}
return re.Hooks.CallOriginal<re.msgpack.Bool>();
}
);
[Msgpack.Gen]
public readonly partial record struct PlayerPuppet_OnGameAttached(PlayerPuppet self);
Vehicle quirks¶
If two players mount a vehicle, the driver gets out and the passenger shuffles over and then gets out, the vehicle sometimes "locks up". This is due to a bug triggering a function that removes interactions from vehicles, originally designed for the story mode tank. Hook this function to prevent the behaviour:
re.Hooks.PlaceVoid(
"VehicleComponent",
"ToggleTargetingSystemForPanzer;PlayerPuppetBool",
(VehicleComponent_ToggleTargetingSystemForPanzer x) =>
{
return;
}
);
[Msgpack.Gen]
public readonly partial record struct VehicleComponent_ToggleTargetingSystemForPanzer(
PlayerDevelopmentSystem self,
PlayerPuppet? mountedPlayer,
bool enable
);