Character dimensions

From Wiki G1R-MP G1 Remake Multiplayer
Jump to navigation Jump to search

Character dimensions

Available since 0.1.8 / Protocol 40. Both client and server must use the matching 0.1.8 build.

Dimensions separate players, scripted human NPCs and monsters on the same map. A character can be visible and interact with another character only in the same dimension; the normal streaming distance still applies. Dimension 0 is the default.

This is useful for a private character creator, preview NPCs and isolated character encounters. It does not load a different map or create private copies of the entire world.

Server-side Lua API

Entity Change dimension Read dimension Identifier
Player setPlayerDimension getPlayerDimension Numeric player ID
Human NPC setNpcDimension getNpcDimension Numeric NPC ID returned by spawnNpc
Monster setMonsterDimension getMonsterDimension Decimal string ID returned by spawnMonster

Dimensions are integer Lua numbers from 0 through 2147483647. Negative, fractional, non-finite and string values are rejected. There is no separate dimension-creation function: assign the same number to the characters that should share a space.

Setters return true when accepted, including an unchanged dimension, or false on failure. Getters return the dimension number, including 0, or false for an invalid/missing entity. Only the resource that owns an NPC/monster can move it between dimensions. Reading a dimension does not grant ownership.

What is separated

  • Character visibility, movement, nameplates and attached appearance; existing distance-based streaming remains active.
  • Character-to-character collision, melee damage, arrows/bolts and spells.
  • NPC/monster perception, pursuit and player-target validation.
  • Proximity microphone voice. Global/channel voice remains explicitly global/channel-based.
  • getPlayersInRange now searches within the source player's dimension. The bundled GothicRP local chat uses this query; GothicRP trade also checks dimensions at invitation, acceptance and completion.

What stays shared

  • Doors, gates, torches, pickups, world items, map objects, terrain, time and weather.
  • Static and custom world/navigation blockers; these are not per-dimension in this stage.
  • getPlayers, the global roster and explicitly global messages/events.
  • getPlayersWithinRange is still a coordinate-based query without an implicit dimension. getDistanceBetweenPlayers still reports geometric distance. If your resource uses these for interactions, compare the participants' dimensions separately.
  • Custom resource events, TTS and script-controlled audio. The script must choose its recipients and validate dimensions when required; dimension changes do not automatically stop previously started custom audio.

A dimension is not a complete sandbox or invulnerability. A trusted server script can still perform its ordinary gameplay operations. A hidden player must not be able to operate another character through an insecure custom event: validate the real event sender, target, distance, dimension and permissions on the server. A private creator should separately manage freezing, camera, UI and shared-world interaction restrictions.

Preserving appearance

Changing dimension does not reconnect, change the player's ID, teleport or replace the local character. Existing identity, body/head, skin colour, tattoos, armor, weapons, health and scripted animation state are sent through the normal character-streaming system when an observer becomes eligible. This also applies to late join and re-entry; observers need not wait for the character to move or change equipment.

Use the existing player customization and NPC appearance APIs normally. setNpcIdentity, setNpcSkinColor and setNpcTattoo do not require dimension-specific variants. NPC/monster dimension changes clear the previous combat target/attack but preserve stats, appearance, equipment and authored route/movement configuration.

Safe player transitions

setPlayerDimension can return false while the player has:

  • A pending dimension transition or authoritative position change.
  • An active interaction, climb or owned mount lease. A mount lease can remain after dismount; remove the owned mount before switching if necessary.
  • An unavailable/incompatible client or a connection that is leaving.

An unchanged dimension succeeds without despawning/recreating actors or emitting another change event. A successful change retires old character attack/projectile state without resetting ammunition, mana or equipment. Input sampled before the transition cannot become a new attack/movement command in the destination dimension.

The return value means server acceptance, not that all clients have rendered the new scene. Do not submit changes every frame or immediately chain several different dimensions. Let your creator state machine handle rejection and retry after the conflicting operation has finished. Proximity audio already buffered before the change can finish briefly.

Example: a private creator session

This example belongs in a trusted server script. The gamemode must allocate a unique dimension for each creator session; choose an allocation scheme shared with your other resources. Player IDs are not a globally reserved dimension range.

local previousDimension = {}

function enterCreator(playerId, allocatedDimension)
    if previousDimension[playerId] ~= nil then return false end
    local oldDimension = getPlayerDimension(playerId)
    if oldDimension == false then return false end
    if not setPlayerDimension(playerId, allocatedDimension) then
        return false -- busy or unavailable: do not open the creator yet
    end
    previousDimension[playerId] = oldDimension
    -- Apply your existing identity/skin/tattoo, camera and GUI logic here.
    return true
end

function leaveCreator(playerId)
    local oldDimension = previousDimension[playerId]
    if oldDimension == nil then return false end
    if not setPlayerDimension(playerId, oldDimension) then
        return false -- keep the session so your state machine can retry
    end
    previousDimension[playerId] = nil
    -- Close your creator GUI and restore camera/input policy separately.
    return true
end

addEventHandler("onPlayerQuit", root, function(playerId)
    previousDimension[playerId] = nil
end)

Also handle cancellation and resource shutdown in your gamemode. Player dimensions are not automatically restored when a resource stops. A new connection starts at dimension 0; database persistence is a gamemode decision. Do not accept an arbitrary dimension from an untrusted client event without checking ownership of the creator session.

Spawn directly into a private dimension

Pass options.dimension to spawnNpc or spawnMonster. This avoids briefly exposing a new actor in dimension 0 before a subsequent setter. Coordinates remain metres and yaw remains degrees.

local dimension = getPlayerDimension(playerId)
local position = getPlayerPosition(playerId)
if dimension ~= false and position then
    local npcId = spawnNpc("Creator preview", "milten",
        position.x + 4, position.y, position.z, 0,
        {dimension = dimension})
    if npcId then
        setNpcIdentity(npcId, "gorn")
        setNpcSkinColor(npcId, 110, 75, 50)
        setNpcTattoo(npcId, "templar1")
    end

    local monsterId = spawnMonster("wolf",
        position.x - 4, position.y, position.z, 0,
        {dimension = dimension, aggressive = false})
    -- Keep these IDs and destroy owned preview actors when finished.
end

If dimension is omitted, spawn behavior remains dimension 0. Existing placement/type/collision validation still applies and spawning can return false.

Change events

Server-local events, registered on root:

They fire after an actual successful server-side change, not on an unchanged value, failed request or ordinary initial spawn. They are not client rendering acknowledgements. Do not recursively change dimensions in these handlers without a terminating condition.

Multiplayer acceptance checklist

Use at least two clients, preferably three. Test appearance and animations in the game, not only Lua return values.

  1. Stand together in dimension 0. Move A to 100: A and B must disappear from each other's view without changing A's position/ID.
  2. Move C to 100: A and C see each other; B remains separated. Verify skin, head, tattoos, armor, weapons and nameplates.
  3. Create NPCs/monsters directly in 100. Move them between 0 and 100 and verify their full appearance and animation on each entry.
  4. Test late join, repeated transitions with short pauses, same-position collisions, attacks/projectiles and proximity voice.
  5. Return to 0 and verify ordinary character interaction. Shared-world doors/items must not have been reset.