2026-09-15 18:13:46 +07:00
|
|
|
|
using Ananta.Server.RpcTypes.Client4229938;
|
|
|
|
|
|
using SceneMethods = Ananta.Server.RpcTypes.Client4229938.Methods.GameScene;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Ananta.Server.Protocol.Client4229938;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Typed factories for world/game-scene messages. The wire layout itself lives in Ananta.RpcTypes.
|
|
|
|
|
|
/// Large world/profile packets are built as RpcTypes DTOs by RuntimePayloadFactory and serialized on the fly.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal static class WorldCodec
|
|
|
|
|
|
{
|
|
|
|
|
|
internal static SceneMethods.SyncLogicAgentEnter LogicAgentEnter(ulong agentId)
|
|
|
|
|
|
=> new() { agentId = agentId };
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncLogicAgentLeave LogicAgentLeave(ulong agentId)
|
|
|
|
|
|
=> new() { agentId = agentId };
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncManagedLogicAgent ManagedLogicAgent(ulong agentId, ulong pid, byte moveId = 0)
|
|
|
|
|
|
=> new() { agentId = agentId, pid = pid, moveId = moveId };
|
|
|
|
|
|
|
2026-10-29 02:44:24 +07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// S2C 68838628 <c>SyncLogicAgentSyncData</c> —— 给「服务端驱动」单位回正位姿。
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ⛑⛑ 它**不能驱动步态**(donor1 第 39 轮 + 我们 09-20 实测已证伪:10Hz 推了 6280 条,
|
|
|
|
|
|
/// 字节布局与客户端上报逐字节对齐,人依然纹丝不动),但正是这个「纹丝不动」
|
|
|
|
|
|
/// 才是我们要的:留在大世界里的旧角色一旦没人管就会自由落体掉出世界
|
|
|
|
|
|
///(2026-09-24 18:1x 实测 Y 从 -0.13 → -293 → -3000,约 60~90 m/s),
|
|
|
|
|
|
/// 因为客户端停掉自己的 AI 后只做物理模拟,而这些 actor 没有地面吸附。
|
|
|
|
|
|
/// ⇒ 用这个包把记录里的站立位姿回给它,它就不会掉。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal static SceneMethods.SyncLogicAgentSyncData4229938 LogicAgentSyncData(
|
|
|
|
|
|
ulong agentId, Vec3 position, float facing, byte moveId = 1)
|
|
|
|
|
|
=> new()
|
|
|
|
|
|
{
|
|
|
|
|
|
AgentId = agentId,
|
|
|
|
|
|
Position = new SceneMethods.UxVector3(position.X, position.Y, position.Z),
|
|
|
|
|
|
Rotation = new SceneMethods.UxVector3(0f, facing, 0f),
|
|
|
|
|
|
MoveId = moveId,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Round 39 (donor1): <c>SyncLogicAgentCommand</c> (mid 68050574) carrying a <b>movement task</b> -
|
|
|
|
|
|
/// <c>TaskWayPointMoveCommandData</c> (wire tag 39). It carries speed, move action and animation
|
|
|
|
|
|
/// group with the waypoints, so the client needs no navigation data at all to run the walk.
|
|
|
|
|
|
/// ★ donor1 round-51 用户亲测的"完整路人"驱动协议(一直有走路动画、会移动、到目的地停下)。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal static SceneMethods.SyncLogicAgentCommandWayPoint LogicAgentCommandWayPoint(
|
|
|
|
|
|
ulong agentId, int commandId, byte specificMethod, float speed, uint moveActionId, uint moveActionGroup,
|
|
|
|
|
|
bool keepMovingAction, IReadOnlyList<SceneMethods.UxVector3> points, bool noRootMotion = true)
|
|
|
|
|
|
=> new()
|
|
|
|
|
|
{
|
|
|
|
|
|
agentId = agentId,
|
|
|
|
|
|
commandId = commandId,
|
|
|
|
|
|
data = new SceneMethods.TaskWayPointMoveCommandData
|
|
|
|
|
|
{
|
|
|
|
|
|
SpecificMethod = specificMethod,
|
|
|
|
|
|
KeepMovingAction = keepMovingAction,
|
|
|
|
|
|
Speed = speed,
|
|
|
|
|
|
MoveActionId = moveActionId,
|
|
|
|
|
|
NoRootMotion = noRootMotion,
|
|
|
|
|
|
MoveActionGroup = moveActionGroup,
|
|
|
|
|
|
WayPoints = points.ToList(),
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-09-15 18:13:46 +07:00
|
|
|
|
internal static SceneMethods.SyncPlayerCurrentSpirit CurrentSpirit()
|
|
|
|
|
|
=> CurrentSpirit(Profile.PlayerPid, Profile.InitialSpiritTemplateId, Profile.InitialUnitId);
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncPlayerCurrentSpirit CurrentSpirit(ulong playerPid, uint templateId, ulong spiritId, bool isAgentSwitch = false)
|
|
|
|
|
|
=> new()
|
|
|
|
|
|
{
|
|
|
|
|
|
playerPid = playerPid,
|
|
|
|
|
|
templateId = templateId,
|
|
|
|
|
|
spiritId = spiritId,
|
|
|
|
|
|
isAgentSwitch = isAgentSwitch
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-10-29 02:44:24 +07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 切人演出的收尾握手之一:告诉客户端"新角色就在原地接管"(2 字段版本,见
|
|
|
|
|
|
/// <c>WorldMethods.cs</c> 里 <c>SyncPlayerSwitchSpiritInSitu</c> 的注释)。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal static SceneMethods.SyncPlayerSwitchSpiritInSitu SwitchSpiritInSitu(uint templateId, ulong spiritId)
|
|
|
|
|
|
=> new()
|
|
|
|
|
|
{
|
|
|
|
|
|
templateId = templateId,
|
|
|
|
|
|
spiritId = spiritId
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// <c>SyncManagedSpirit</c> / <c>SyncManagedAgent</c> 共用的包体构造。
|
|
|
|
|
|
/// CBT2 官方服务端这里传的是 <c>moveId = 0</c>。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal static SceneMethods.SyncManagedUnit4229938 ManagedUnit(ulong pid, ulong id, int moveId = 0)
|
|
|
|
|
|
=> new()
|
|
|
|
|
|
{
|
|
|
|
|
|
pid = pid,
|
|
|
|
|
|
id = id,
|
|
|
|
|
|
moveId = moveId
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-09-15 18:13:46 +07:00
|
|
|
|
internal static SceneMethods.SyncSwitchSpiritConfigId SwitchSpiritConfigId(
|
|
|
|
|
|
uint configId, Vec3 position, IReadOnlyCollection<ulong>? spawnedAgentIds = null)
|
|
|
|
|
|
=> new()
|
|
|
|
|
|
{
|
|
|
|
|
|
configId = configId,
|
|
|
|
|
|
position = new SceneMethods.UxVector3(position.X, position.Y, position.Z),
|
|
|
|
|
|
spawnedAgentIds = spawnedAgentIds?.ToList() ?? []
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncPreSwitchSpirit PreSwitchSpirit(uint configId, uint newTemplateId, Vec3 position)
|
|
|
|
|
|
=> new()
|
|
|
|
|
|
{
|
|
|
|
|
|
configId = configId,
|
|
|
|
|
|
newTemplateId = newTemplateId,
|
|
|
|
|
|
position = new SceneMethods.UxVector3(position.X, position.Y, position.Z)
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncPlayerLoadRate PlayerLoadRate(double rate = 1.0)
|
|
|
|
|
|
=> new() { playerPid = Profile.PlayerPid, rate = rate };
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncSceneLoadCompleted SceneLoadCompleted(ulong sceneId)
|
|
|
|
|
|
=> new() { sceneId = sceneId };
|
|
|
|
|
|
|
2026-10-29 02:44:24 +07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Hard snap: the unit is placed and its locomotion state is explicitly "none" (MoveAction.Unknown).
|
|
|
|
|
|
/// Correct for teleports and first-time placement — wrong for anything that is supposed to walk,
|
|
|
|
|
|
/// because a unit moved this way slides to the target without ever playing a gait.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal static SceneMethods.SyncUnitPositionAndFacing PositionAndFacing(ulong unitId, Vec3 position, float facing, byte setPositionType = SetPositionType.SwitchSpirit)
|
|
|
|
|
|
=> PositionAndFacing(unitId, position, facing, setPositionType, continueMove: false, moveId: MoveAction.Unknown);
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Position push that also tells the client <b>which locomotion state the unit is in</b>.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// <paramref name="moveId"/> is a <c>UX.Game.BattleMoveActionType</c> and is the field the client's
|
|
|
|
|
|
/// locomotion state machine reads. 0 (Unknown) means "no gait", so an entity moved this way slides
|
|
|
|
|
|
/// to the new position without playing a walk/run cycle — the "走路僵硬 / 瞬移" symptom. Every
|
|
|
|
|
|
/// overload here used to hardcode <c>moveId = 0</c>, which made any server-driven movement look
|
|
|
|
|
|
/// stiff no matter what the caller intended.
|
|
|
|
|
|
/// </summary>
|
2026-09-18 19:05:29 +07:00
|
|
|
|
internal static SceneMethods.SyncUnitPositionAndFacing PositionAndFacing(
|
|
|
|
|
|
ulong unitId, Vec3 position, float facing, byte setPositionType, bool continueMove, byte moveId)
|
2026-09-15 18:13:46 +07:00
|
|
|
|
=> new()
|
|
|
|
|
|
{
|
|
|
|
|
|
unitId = unitId,
|
|
|
|
|
|
position = new SceneMethods.UxVector3(position.X, position.Y, position.Z),
|
|
|
|
|
|
facing = facing,
|
2026-09-18 19:05:29 +07:00
|
|
|
|
moveId = moveId,
|
|
|
|
|
|
continueMove = continueMove,
|
2026-09-15 18:13:46 +07:00
|
|
|
|
setPositionType = setPositionType,
|
|
|
|
|
|
moveGroundInfo = null,
|
|
|
|
|
|
loadingInfo = null,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-10-29 02:44:24 +07:00
|
|
|
|
/// <summary>Walking push: gait = WalkFront, GM positioning, keep the locomotion state alive on arrival.</summary>
|
2026-09-18 19:05:29 +07:00
|
|
|
|
internal static SceneMethods.SyncUnitPositionAndFacing Walking(
|
2026-10-29 02:44:24 +07:00
|
|
|
|
ulong unitId, Vec3 position, float facing,
|
|
|
|
|
|
byte moveId = MoveAction.WalkFront,
|
|
|
|
|
|
byte setPositionType = SetPositionType.Gm,
|
|
|
|
|
|
bool continueMove = true)
|
2026-09-18 19:05:29 +07:00
|
|
|
|
=> PositionAndFacing(unitId, position, facing, setPositionType, continueMove, moveId);
|
|
|
|
|
|
|
2026-10-29 02:44:24 +07:00
|
|
|
|
internal static SceneMethods.SyncUnitPositionAndFacing PositionAndFacing(Vec3 position, float facing, byte setPositionType = SetPositionType.SwitchSpirit)
|
2026-09-15 18:13:46 +07:00
|
|
|
|
=> PositionAndFacing(Profile.InitialUnitId, position, facing, setPositionType);
|
|
|
|
|
|
|
2026-10-29 02:44:24 +07:00
|
|
|
|
internal static SceneMethods.SyncUnitPositionP PositionP(ulong unitId, Vec3 position, float facing, byte setPositionType = SetPositionType.SwitchSpirit)
|
|
|
|
|
|
=> PositionP(unitId, position, facing, setPositionType, MoveAction.Unknown);
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// <see cref="SyncUnitPositionP"/> carries a moveId too, so it has the same "no gait" trap as
|
|
|
|
|
|
/// <see cref="PositionAndFacing(ulong, Vec3, float, byte)"/>.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal static SceneMethods.SyncUnitPositionP PositionP(ulong unitId, Vec3 position, float facing, byte setPositionType, byte moveId)
|
2026-09-15 18:13:46 +07:00
|
|
|
|
=> new()
|
|
|
|
|
|
{
|
|
|
|
|
|
unitId = unitId,
|
|
|
|
|
|
position = new SceneMethods.UxVector3(position.X, position.Y, position.Z),
|
|
|
|
|
|
facing = facing,
|
2026-10-29 02:44:24 +07:00
|
|
|
|
moveId = moveId,
|
2026-09-15 18:13:46 +07:00
|
|
|
|
setPositionType = setPositionType
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-10-29 02:44:24 +07:00
|
|
|
|
internal static SceneMethods.SyncUnitPositionP PositionP(Vec3 position, float facing, byte setPositionType = SetPositionType.SwitchSpirit)
|
2026-09-15 18:13:46 +07:00
|
|
|
|
=> PositionP(Profile.InitialUnitId, position, facing, setPositionType);
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncGamePause GamePause(bool pause)
|
|
|
|
|
|
=> new() { pause = pause };
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncEntityActionGroup EntityActionGroup(ulong unitId, uint actionGroupId = 1u)
|
|
|
|
|
|
=> new() { unitId = unitId, actionGroupId = actionGroupId };
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncAllSpiritCombatPower AllSpiritCombatPower(uint templateId, float combatPower = 1000f)
|
|
|
|
|
|
=> new() { combatPower = new Dictionary<uint, float> { [templateId] = combatPower } };
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncAllSpiritCombatPower AllSpiritCombatPower(params uint[] templateIds)
|
|
|
|
|
|
{
|
|
|
|
|
|
var body = new SceneMethods.SyncAllSpiritCombatPower();
|
|
|
|
|
|
foreach (var templateId in templateIds)
|
|
|
|
|
|
body.combatPower[templateId] = 1000f;
|
|
|
|
|
|
return body;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncChangeSkill SkillBinding(ulong spiritId, uint skillId)
|
|
|
|
|
|
=> new()
|
|
|
|
|
|
{
|
|
|
|
|
|
spiritId = spiritId,
|
|
|
|
|
|
skillId = skillId,
|
|
|
|
|
|
duration = skillId == 0 ? 0f : 99999f
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncUnitBuffList UnitBuffList(ulong unitId, IReadOnlyList<uint> buffIds, uint firstInstanceId)
|
|
|
|
|
|
{
|
|
|
|
|
|
var body = new SceneMethods.SyncUnitBuffList { entityId = unitId };
|
|
|
|
|
|
// 4229938 still evaluates ExpireTime inside several client-side BuffActions even when
|
|
|
|
|
|
// Permanent=true. ExpireTime=0 becomes a huge negative remaining time and can execute
|
|
|
|
|
|
// teardown paths immediately. Use a real far-future unix timestamp for persistent state.
|
|
|
|
|
|
var persistentExpiry = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 315_360_000d;
|
|
|
|
|
|
var instanceId = firstInstanceId;
|
|
|
|
|
|
foreach (var buffId in buffIds)
|
|
|
|
|
|
{
|
|
|
|
|
|
body.buffs.Add(new SceneMethods.BuffViewData
|
|
|
|
|
|
{
|
|
|
|
|
|
InstanceId = instanceId++,
|
|
|
|
|
|
Id = buffId,
|
|
|
|
|
|
ReleaserId = unitId,
|
|
|
|
|
|
ExpireTime = persistentExpiry,
|
|
|
|
|
|
Tier = 1,
|
|
|
|
|
|
Permanent = true,
|
|
|
|
|
|
DestructibleId = 0
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
return body;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncUnitAddBuff UnitAddBuff(ulong unitId, uint buffId, uint instanceId)
|
|
|
|
|
|
=> new()
|
|
|
|
|
|
{
|
|
|
|
|
|
entityId = unitId,
|
|
|
|
|
|
buff = new SceneMethods.BuffViewData
|
|
|
|
|
|
{
|
|
|
|
|
|
InstanceId = instanceId,
|
|
|
|
|
|
Id = buffId,
|
|
|
|
|
|
ReleaserId = unitId,
|
|
|
|
|
|
ExpireTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 315_360_000d,
|
|
|
|
|
|
Tier = 1,
|
|
|
|
|
|
Permanent = true,
|
|
|
|
|
|
DestructibleId = 0
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncUnitRemoveBuff UnitRemoveBuff(ulong unitId, uint buffInstanceId)
|
|
|
|
|
|
=> new() { entityId = unitId, buffInstanceId = buffInstanceId };
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncUnitStates UnitStates(ulong unitId, params uint[] states)
|
|
|
|
|
|
=> new() { unitId = unitId, states = states?.ToList(), effectFreezeState = 0 };
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncUnitStates UnitStates(params uint[] states)
|
|
|
|
|
|
=> UnitStates(Profile.InitialUnitId, states);
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncRemoveUnitState RemoveUnitState(ulong unitId, uint state)
|
|
|
|
|
|
=> new() { unitId = unitId, state = state, reason = 1, sourceId = 0 };
|
|
|
|
|
|
|
|
|
|
|
|
internal static SceneMethods.SyncRemoveUnitState RemoveUnitState(uint state)
|
|
|
|
|
|
=> RemoveUnitState(Profile.InitialUnitId, state);
|
2026-09-18 19:05:29 +07:00
|
|
|
|
|
2026-10-29 02:44:24 +07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// <c>UX.Game.BattleMoveActionType</c> — authoritative values from
|
|
|
|
|
|
/// <c>ux_rpc/ux_game_all.dump.cs:21040</c> (IL2CPP TypeDefIndex 32492).
|
|
|
|
|
|
///
|
|
|
|
|
|
/// This is the <c>moveId</c> byte of <c>SyncUnitPositionAndFacing</c>, <c>SyncUnitPositionP</c> and
|
|
|
|
|
|
/// <c>SyncManagedLogicAgent</c>, and of every <c>BattleMoveDataBase</c> subclass. The enum itself is
|
|
|
|
|
|
/// an <c>int</c> in the client, but the wire field is a single byte.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Unknown (0) means "no locomotion state" — a unit pushed with it slides instead of walking, which
|
|
|
|
|
|
/// is why server-driven movement used to look stiff.
|
|
|
|
|
|
/// </summary>
|
2026-09-18 19:05:29 +07:00
|
|
|
|
internal static class MoveAction
|
|
|
|
|
|
{
|
|
|
|
|
|
internal const byte Unknown = 0;
|
|
|
|
|
|
internal const byte Idle = 1;
|
|
|
|
|
|
internal const byte WalkFront = 2;
|
|
|
|
|
|
internal const byte WalkBack = 3;
|
|
|
|
|
|
internal const byte WalkLeft = 4;
|
|
|
|
|
|
internal const byte WalkRight = 5;
|
|
|
|
|
|
internal const byte Run = 6;
|
2026-10-29 02:44:24 +07:00
|
|
|
|
|
|
|
|
|
|
internal const byte Min = Unknown;
|
|
|
|
|
|
internal const byte Max = Run;
|
|
|
|
|
|
|
|
|
|
|
|
internal static readonly (byte Id, string Name)[] All =
|
|
|
|
|
|
[
|
|
|
|
|
|
(Unknown, "Unknown 空"),
|
|
|
|
|
|
(Idle, "Idle 待机"),
|
|
|
|
|
|
(WalkFront, "WalkFront 前进"),
|
|
|
|
|
|
(WalkBack, "WalkBack 后退"),
|
|
|
|
|
|
(WalkLeft, "WalkLeft 左移"),
|
|
|
|
|
|
(WalkRight, "WalkRight 右移"),
|
|
|
|
|
|
(Run, "Run 奔跑"),
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
internal static string NameOf(byte moveId)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var entry in All)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (entry.Id == moveId)
|
|
|
|
|
|
return entry.Name;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return $"MoveId {moveId}";
|
|
|
|
|
|
}
|
2026-09-18 19:05:29 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-29 02:44:24 +07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// <c>UX.Game.SetPositionTypeForClient</c> — authoritative values from
|
|
|
|
|
|
/// <c>ux_rpc/ux_game_all.dump.cs:15656</c> (IL2CPP TypeDefIndex 32205).
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ⚠️ <c>Gm</c> is NOT a prerequisite for moving the main character: the value that matters less
|
|
|
|
|
|
/// than the packet. Moving the local avatar via <c>SyncUnitPosition_P</c> fails no matter which
|
|
|
|
|
|
/// type is used (NetworkGhostModule demands NetworkRoleType.Proxy); use
|
|
|
|
|
|
/// <c>SyncUnitPositionAndFacing</c> with the default <c>SwitchSpirit</c> instead — that is what
|
|
|
|
|
|
/// the donor server's own teleport API does and it lands the player.
|
|
|
|
|
|
/// </summary>
|
2026-09-18 19:05:29 +07:00
|
|
|
|
internal static class SetPositionType
|
|
|
|
|
|
{
|
|
|
|
|
|
internal const byte Force = 0;
|
|
|
|
|
|
internal const byte RejectSync = 1;
|
|
|
|
|
|
internal const byte Revive = 2;
|
|
|
|
|
|
internal const byte SwitchSpirit = 3;
|
|
|
|
|
|
internal const byte SpoonNoLoading = 4;
|
|
|
|
|
|
internal const byte FallGround = 5;
|
|
|
|
|
|
internal const byte Teleport = 6;
|
|
|
|
|
|
internal const byte Gm = 7;
|
|
|
|
|
|
internal const byte Portal = 8;
|
|
|
|
|
|
internal const byte OutOfStuck = 9;
|
|
|
|
|
|
}
|
2026-09-15 18:13:46 +07:00
|
|
|
|
}
|