NorbiPeti
f403feb298
Removed BlockIdentifiers.OWNED_BLOCKS as the original got replaced with an array Added the correct group for each supported functional block Removed EntityFactory property from IEntitySerializer as it is provided on deserialization
485 lines
14 KiB
C#
485 lines
14 KiB
C#
using System;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
using RobocraftX.Character;
|
|
using RobocraftX.Character.Movement;
|
|
using RobocraftX.Common.Players;
|
|
using RobocraftX.Common.Input;
|
|
using RobocraftX.CR.MachineEditing.BoxSelect;
|
|
using RobocraftX.Physics;
|
|
using RobocraftX.Blocks.Ghost;
|
|
using RobocraftX.Character.Camera;
|
|
using RobocraftX.Character.Factories;
|
|
using Gamecraft.CharacterVulnerability;
|
|
using Gamecraft.CharacterVulnerability.Entities;
|
|
using Svelto.ECS;
|
|
using Unity.Mathematics;
|
|
using Unity.Physics;
|
|
using UnityEngine;
|
|
|
|
using GamecraftModdingAPI.Engines;
|
|
|
|
namespace GamecraftModdingAPI.Players
|
|
{
|
|
internal class PlayerEngine : IApiEngine, IFactoryEngine
|
|
{
|
|
public string Name { get; } = "GamecraftModdingAPIPlayerGameEngine";
|
|
|
|
public EntitiesDB entitiesDB { set; private get; }
|
|
|
|
public bool isRemovable => false;
|
|
|
|
public IEntityFactory Factory { set; private get; }
|
|
|
|
private bool isReady = false;
|
|
|
|
public void Dispose()
|
|
{
|
|
isReady = false;
|
|
}
|
|
|
|
public void Ready()
|
|
{
|
|
isReady = true;
|
|
}
|
|
|
|
public uint GetLocalPlayer()
|
|
{
|
|
if (!isReady) return uint.MaxValue;
|
|
var localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers);
|
|
if (localPlayers.count > 0)
|
|
{
|
|
return localPlayers[0].ID.entityID;
|
|
}
|
|
return uint.MaxValue;
|
|
}
|
|
|
|
public uint GetRemotePlayer()
|
|
{
|
|
if (!isReady) return uint.MaxValue;
|
|
var localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers);
|
|
if (localPlayers.count > 0)
|
|
{
|
|
return localPlayers[0].ID.entityID;
|
|
}
|
|
return uint.MaxValue;
|
|
}
|
|
|
|
public long GetAllPlayerCount()
|
|
{
|
|
if (entitiesDB == null) return 0;
|
|
long count = 0;
|
|
foreach (ExclusiveGroupStruct eg in PlayersExclusiveGroups.AllPlayers)
|
|
{
|
|
count += entitiesDB.Count<PlayerIDStruct>(eg);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public long GetLocalPlayerCount()
|
|
{
|
|
if (entitiesDB == null) return 0;
|
|
return entitiesDB.Count<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers);
|
|
}
|
|
|
|
public long GetRemotePlayerCount()
|
|
{
|
|
if (entitiesDB == null) return 0;
|
|
return entitiesDB.Count<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers);
|
|
}
|
|
|
|
public bool ExistsById(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return false;
|
|
return entitiesDB.Exists<PlayerIDStruct>(playerId, PlayersExclusiveGroups.LocalPlayers)
|
|
|| entitiesDB.Exists<PlayerIDStruct>(playerId, PlayersExclusiveGroups.RemotePlayers);
|
|
}
|
|
|
|
public float3 GetLocation(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return float3.zero;
|
|
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
return rbes.position;
|
|
}
|
|
return float3.zero;
|
|
}
|
|
|
|
public bool SetLocation(uint playerId, float3 location, bool exitSeat = true)
|
|
{
|
|
if (entitiesDB == null) return false;
|
|
var characterGroups = CharacterExclusiveGroups.AllCharacters;
|
|
for (int i = 0; i < characterGroups.count; i++)
|
|
{
|
|
EGID egid = new EGID(playerId, characterGroups[i]);
|
|
if (entitiesDB.Exists<RigidBodyEntityStruct>(egid))
|
|
{
|
|
ref RigidBodyEntityStruct rbes = ref entitiesDB.QueryEntity<RigidBodyEntityStruct>(egid);
|
|
if (characterGroups[i] == CharacterExclusiveGroups.InPilotSeatGroup && exitSeat)
|
|
{
|
|
entitiesDB.QueryEntity<CharacterPilotSeatEntityStruct>(egid).instantExit = true;
|
|
entitiesDB.PublishEntityChange<CharacterPilotSeatEntityStruct>(egid);
|
|
}
|
|
rbes.position = location;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public float3 GetRotation(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return float3.zero;
|
|
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
return ((Quaternion) rbes.rotation).eulerAngles;
|
|
}
|
|
return default(float3);
|
|
}
|
|
|
|
public bool SetRotation(uint playerId, float3 value)
|
|
{
|
|
if (entitiesDB == null) return false;
|
|
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
Quaternion q = rbes.rotation;
|
|
q.eulerAngles = value;
|
|
rbes.rotation = q;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public float3 GetLinearVelocity(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return float3.zero;
|
|
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
return rbes.velocity;
|
|
}
|
|
return float3.zero;
|
|
}
|
|
|
|
public bool SetLinearVelocity(uint playerId, float3 value)
|
|
{
|
|
if (entitiesDB == null) return false;
|
|
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
rbes.velocity = value;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public float3 GetAngularVelocity(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return float3.zero;
|
|
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
return rbes.angularVelocity;
|
|
}
|
|
return float3.zero;
|
|
}
|
|
|
|
public bool SetAngularVelocity(uint playerId, float3 value)
|
|
{
|
|
if (entitiesDB == null) return false;
|
|
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
rbes.angularVelocity = value;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public PhysicsMass GetMass(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return default(PhysicsMass);
|
|
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
return rbes.physicsMass;
|
|
}
|
|
return default(PhysicsMass);
|
|
}
|
|
|
|
public bool SetInverseMass(uint playerId, float inverseMass)
|
|
{
|
|
if (entitiesDB == null) return false;
|
|
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
rbes.physicsMass.InverseInertia = inverseMass;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public float? GetLastPingTime(uint playerId, PlayerType type)
|
|
{
|
|
if (entitiesDB == null) return null;
|
|
EGID egid = new EGID(playerId, PlayerGroupFromEnum(type));
|
|
if (entitiesDB.Exists<PlayerNetworkStatsEntityStruct>(egid))
|
|
{
|
|
return entitiesDB.QueryEntity<PlayerNetworkStatsEntityStruct>(egid).lastPingTimeSinceLevelLoad;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public float GetInitialHealth(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return 0;
|
|
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
return c.initialHealth;
|
|
}
|
|
return -1f;
|
|
}
|
|
|
|
public bool SetInitialHealth(uint playerId, float val)
|
|
{
|
|
if (entitiesDB == null) return false;
|
|
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
c.initialHealth = val;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public float GetCurrentHealth(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return 0;
|
|
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
return c.currentHealth;
|
|
}
|
|
return -1f;
|
|
}
|
|
|
|
public bool SetCurrentHealth(uint playerId, float val)
|
|
{
|
|
if (entitiesDB == null) return false;
|
|
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
c.currentHealth = val;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool DamagePlayer(uint playerId, float amount)
|
|
{
|
|
if (entitiesDB == null) return false;
|
|
Factory.BuildEntity<DamageEntityDescriptor>(
|
|
new EGID(CharacterVulnerabilityExclusiveGroups.NextDamageEntityId, CharacterVulnerabilityExclusiveGroups.CharacterDamageExclusiveGroup)
|
|
).Init(new DamageEntityStruct
|
|
{
|
|
damage = amount,
|
|
targetPlayerEntityId = playerId,
|
|
});
|
|
return true;
|
|
}
|
|
|
|
public bool GetDamageable(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return false;
|
|
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
return c.canTakeDamageStat;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool SetDamageable(uint playerId, bool val)
|
|
{
|
|
if (entitiesDB == null) return false;
|
|
ref var ches = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
ches.canTakeDamage = val;
|
|
ches.canTakeDamage = val;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public uint GetInitialLives(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return 0;
|
|
ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
return c.initialLives;
|
|
}
|
|
return uint.MaxValue;
|
|
}
|
|
|
|
public bool SetInitialLives(uint playerId, uint val)
|
|
{
|
|
if (entitiesDB == null) return false;
|
|
ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
c.initialLives = val;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public uint GetCurrentLives(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return 0;
|
|
ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
return c.currentLives;
|
|
}
|
|
return uint.MaxValue;
|
|
}
|
|
|
|
public bool SetCurrentLives(uint playerId, uint val)
|
|
{
|
|
if (entitiesDB == null) return false;
|
|
ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
c.currentLives = val;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool GetGameOverScreen(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return false;
|
|
ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
return c.gameOverScreen;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool IsDead(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return true;
|
|
return entitiesDB.Exists<RigidBodyEntityStruct>(playerId, CharacterExclusiveGroups.DeadCharacters);
|
|
}
|
|
|
|
public int GetSelectedBlock(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return 0;
|
|
ref var c = ref GetCharacterStruct<EquippedPartStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
return c.SelectedDBPartID;
|
|
}
|
|
return ushort.MaxValue;
|
|
}
|
|
|
|
public byte GetSelectedColor(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return 0;
|
|
ref var c = ref GetCharacterStruct<EquippedColourStruct>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
return c.indexInPalette;
|
|
}
|
|
return 255;
|
|
}
|
|
|
|
// reusable methods
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private ExclusiveGroup PlayerGroupFromEnum(PlayerType type)
|
|
{
|
|
return type == PlayerType.Local ? PlayersExclusiveGroups.LocalPlayers : PlayersExclusiveGroups.RemotePlayers;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public ref T GetCharacterStruct<T>(uint playerId, out bool exists) where T : unmanaged, IEntityComponent
|
|
{
|
|
var characterGroups = CharacterExclusiveGroups.AllCharacters;
|
|
for (int i = 0; i < characterGroups.count; i++)
|
|
{
|
|
EGID egid = new EGID(playerId, characterGroups[i]);
|
|
if (entitiesDB.Exists<T>(egid))
|
|
{
|
|
exists = true;
|
|
return ref entitiesDB.QueryEntity<T>(egid);
|
|
}
|
|
}
|
|
|
|
exists = false;
|
|
T[] arr = new T[1];
|
|
return ref arr[0]; //Return default value
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public bool GetPlayerStruct<T>(uint playerId, out T s) where T : unmanaged, IEntityComponent
|
|
{
|
|
var playerGroups = PlayersExclusiveGroups.AllPlayers;
|
|
for (int i = 0; i < playerGroups.count; i++)
|
|
{
|
|
EGID egid = new EGID(playerId, playerGroups[i]);
|
|
if (entitiesDB.Exists<T>(egid))
|
|
{
|
|
s = entitiesDB.QueryEntity<T>(egid);
|
|
return true;
|
|
}
|
|
}
|
|
s = default;
|
|
return false;
|
|
}
|
|
|
|
public EGID? GetThingLookedAt(uint playerId, float maxDistance = -1f)
|
|
{
|
|
if (!entitiesDB.TryQueryMappedEntities<CharacterCameraRayCastEntityStruct>(
|
|
CameraExclusiveGroups.CameraGroup, out var mapper))
|
|
return null;
|
|
mapper.TryGetEntity(playerId, out CharacterCameraRayCastEntityStruct rayCast);
|
|
float distance = maxDistance < 0
|
|
? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast,
|
|
GhostBlockUtils.GhostCastMethod.GhostCastProportionalToBlockSize)
|
|
: maxDistance;
|
|
if (rayCast.hit && rayCast.distance <= distance)
|
|
return rayCast.hitEgid;
|
|
|
|
return null;
|
|
}
|
|
|
|
public unsafe Block[] GetSelectedBlocks(uint playerid)
|
|
{
|
|
if (!entitiesDB.Exists<BoxSelectStateEntityStruct>(playerid,
|
|
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup))
|
|
return new Block[0];
|
|
var state = entitiesDB.QueryEntity<BoxSelectStateEntityStruct>(playerid,
|
|
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
|
|
var blocks = entitiesDB.QueryEntity<SelectedBlocksStruct>(playerid,
|
|
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
|
|
if (!state.active) return new Block[0];
|
|
var pointer = (EGID*) blocks.selectedBlocks.ToPointer();
|
|
var ret = new Block[blocks.count];
|
|
for (int j = 0; j < blocks.count; j++)
|
|
{
|
|
var egid = pointer[j];
|
|
ret[j] = new Block(egid);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
}
|