first commit

This commit is contained in:
2025-07-06 00:23:46 +02:00
commit 38f50c8819
1788 changed files with 112878 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
using NitroxModel.Core;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
using NitroxModel.DataStructures.Util;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
public abstract class EntityMetadataExtractor<I, O> : IEntityMetadataExtractor where O : EntityMetadata
{
public abstract O Extract(I entity);
public Optional<EntityMetadata> From(object o)
{
EntityMetadata result = Extract((I)o);
return Optional.OfNullable(result);
}
protected T Resolve<T>() where T : class
{
return NitroxServiceLocator.Cache<T>.Value;
}
}

View File

@@ -0,0 +1,9 @@
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
using NitroxModel.DataStructures.Util;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
public interface IEntityMetadataExtractor
{
public Optional<EntityMetadata> From(object o);
}

View File

@@ -0,0 +1,12 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class BatteryMetadataExtractor : EntityMetadataExtractor<Battery, BatteryMetadata>
{
public override BatteryMetadata Extract(Battery entity)
{
return new(entity._charge);
}
}

View File

@@ -0,0 +1,12 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class BeaconMetadataExtractor : EntityMetadataExtractor<Beacon, BeaconMetadata>
{
public override BeaconMetadata Extract(Beacon beacon)
{
return new(beacon.beaconLabel.GetLabel());
}
}

View File

@@ -0,0 +1,12 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class ConstructorMetadataExtractor : EntityMetadataExtractor<Constructor, ConstructorMetadata>
{
public override ConstructorMetadata Extract(Constructor entity)
{
return new(entity.deployed);
}
}

View File

@@ -0,0 +1,12 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class CrashHomeMetadataExtractor : EntityMetadataExtractor<CrashHome, CrashHomeMetadata>
{
public override CrashHomeMetadata Extract(CrashHome crashHome)
{
return new(crashHome.spawnTime);
}
}

View File

@@ -0,0 +1,12 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class CyclopsLightingMetadataExtractor : EntityMetadataExtractor<CyclopsLightingPanel, CyclopsLightingMetadata>
{
public override CyclopsLightingMetadata Extract(CyclopsLightingPanel lighting)
{
return new(lighting.floodlightsOn, lighting.lightingOn);
}
}

View File

@@ -0,0 +1,41 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxClient.Unity.Helper;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
using UnityEngine;
using static NitroxClient.GameLogic.Spawning.Metadata.Extractor.CyclopsMetadataExtractor;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class CyclopsMetadataExtractor : EntityMetadataExtractor<CyclopsGameObject, CyclopsMetadata>
{
public override CyclopsMetadata Extract(CyclopsGameObject cyclops)
{
GameObject gameObject = cyclops.GameObject;
CyclopsSilentRunningAbilityButton silentRunning = gameObject.RequireComponentInChildren<CyclopsSilentRunningAbilityButton>(true);
CyclopsEngineChangeState engineState = gameObject.RequireComponentInChildren<CyclopsEngineChangeState>(true);
bool engineShuttingDown = (engineState.motorMode.engineOn && engineState.invalidButton);
bool engineOn = (engineState.startEngine || engineState.motorMode.engineOn) && !engineShuttingDown;
CyclopsShieldButton shield = gameObject.GetComponentInChildren<CyclopsShieldButton>(true);
bool shieldOn = (shield) ? shield.active : false;
CyclopsSonarButton sonarButton = gameObject.GetComponentInChildren<CyclopsSonarButton>(true);
bool sonarOn = (sonarButton) ? sonarButton._sonarActive : false;
CyclopsMotorMode.CyclopsMotorModes motorMode = engineState.motorMode.cyclopsMotorMode;
LiveMixin liveMixin = gameObject.RequireComponentInChildren<LiveMixin>();
float health = liveMixin.health;
SubRoot subRoot = gameObject.RequireComponentInChildren<SubRoot>();
bool isDestroyed = subRoot.subDestroyed || health <= 0f;
return new(silentRunning.active, shieldOn, sonarOn, engineOn, (int)motorMode, health, isDestroyed);
}
public struct CyclopsGameObject
{
public GameObject GameObject { get; set; }
}
}

View File

@@ -0,0 +1,16 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class EatableMetadataExtractor : EntityMetadataExtractor<Eatable, EatableMetadata>
{
public override EatableMetadata Extract(Eatable eatable)
{
if (eatable.decomposes)
{
return new(eatable.timeDecayStart);
}
return null;
}
}

View File

@@ -0,0 +1,18 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class EggMetadataExtractor : EntityMetadataExtractor<CreatureEgg, EggMetadata>
{
public override EggMetadata Extract(CreatureEgg creatureEgg)
{
// If the egg is not in a water park (when being picked up or dropped outside of one),
// we only need the exact progress value because progress only increases while inside a water park
if (Items.PickingUpObject == creatureEgg.gameObject || !creatureEgg.insideWaterPark)
{
return new(-1f, creatureEgg.progress);
}
return new(creatureEgg.timeStartHatching, creatureEgg.progress);
}
}

View File

@@ -0,0 +1,14 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxClient.Unity.Helper;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class EscapePodMetadataExtractor : EntityMetadataExtractor<EscapePod, EscapePodMetadata>
{
public override EscapePodMetadata Extract(EscapePod entity)
{
Radio radio = entity.radioSpawner.spawnedObj.RequireComponent<Radio>();
return new EscapePodMetadata(entity.liveMixin.IsFullHealth(), radio.liveMixin.IsFullHealth());
}
}

View File

@@ -0,0 +1,15 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class ExosuitMetadataExtractor : EntityMetadataExtractor<Exosuit, ExosuitMetadata>
{
public override ExosuitMetadata Extract(Exosuit exosuit)
{
LiveMixin liveMixin = exosuit.liveMixin;
SubName subName = exosuit.subName;
return new(liveMixin.health, SubNameInputMetadataExtractor.GetName(subName), SubNameInputMetadataExtractor.GetColors(subName));
}
}

View File

@@ -0,0 +1,12 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class FireExtinguisherHolderMetadataExtractor : EntityMetadataExtractor<FireExtinguisherHolder, FireExtinguisherHolderMetadata>
{
public override FireExtinguisherHolderMetadata Extract(FireExtinguisherHolder entity)
{
return new(entity.hasTank, entity.fuel);
}
}

View File

@@ -0,0 +1,17 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class FlareMetadataExtractor : EntityMetadataExtractor<Flare, FlareMetadata>
{
public override FlareMetadata Extract(Flare flare)
{
// If the flare is thrown, set its activation time
if (flare.flareActiveState && Items.PickingUpObject != flare.gameObject)
{
return new(flare.energyLeft, flare.hasBeenThrown, flare.flareActivateTime);
}
return new(flare.energyLeft, flare.hasBeenThrown, null);
}
}

View File

@@ -0,0 +1,15 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxClient.Unity.Helper;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class FlashlightMetadataExtractor : EntityMetadataExtractor<FlashLight, FlashlightMetadata>
{
public override FlashlightMetadata Extract(FlashLight entity)
{
ToggleLights lights = entity.RequireComponent<ToggleLights>();
return new(lights.lightsActive);
}
}

View File

@@ -0,0 +1,22 @@
using System.Linq;
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class FruitPlantMetadataExtractor : EntityMetadataExtractor<FruitPlant, FruitPlantMetadata>
{
public override FruitPlantMetadata Extract(FruitPlant fruitPlant)
{
bool[] prefabsPicked = fruitPlant.fruits.Select(prefab => prefab.pickedState).ToArray();
// If fruit spawn is disabled (certain plants like kelp don't regrow their fruits) and if none of the fruits were picked (all picked = false)
// then we don't need to save this data because the plant is spawned like this by default
if (!fruitPlant.fruitSpawnEnabled && prefabsPicked.All(b => !b))
{
return null;
}
return new(prefabsPicked, fruitPlant.timeNextFruit);
}
}

View File

@@ -0,0 +1,23 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class PlantableMetadataExtractor(FruitPlantMetadataExtractor fruitPlantMetadataExtractor) : EntityMetadataExtractor<Plantable, PlantableMetadata>
{
private readonly FruitPlantMetadataExtractor fruitPlantMetadataExtractor = fruitPlantMetadataExtractor;
public override PlantableMetadata Extract(Plantable plantable)
{
// Default value for no progress is -1
PlantableMetadata metadata = new(plantable.growingPlant ? plantable.growingPlant.timeStartGrowth : -1, plantable.GetSlotID());
// TODO: Refer to the TODO in PlantableMetadata
if (plantable.linkedGrownPlant && plantable.linkedGrownPlant.TryGetComponent(out FruitPlant fruitPlant))
{
metadata.FruitPlantMetadata = fruitPlantMetadataExtractor.Extract(fruitPlant);
}
return metadata;
}
}

View File

@@ -0,0 +1,36 @@
using System.Collections.Generic;
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
using NitroxModel_Subnautica.DataStructures;
using static NitroxModel.DataStructures.GameLogic.Entities.Metadata.PlayerMetadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class PlayerMetadataExtractor : EntityMetadataExtractor<Player, PlayerMetadata>
{
public override PlayerMetadata Extract(Player player)
{
return new PlayerMetadata(ExtractEquippedItems());
}
private List<EquippedItem> ExtractEquippedItems()
{
Equipment equipment = Inventory.main.equipment;
List<EquippedItem> equipped = new();
foreach (KeyValuePair<string, InventoryItem> slotWithItem in equipment.equipment)
{
InventoryItem item = slotWithItem.Value;
// not every slot will always contain an item.
if (item != null && item.item.TryGetIdOrWarn(out NitroxId itemId))
{
equipped.Add(new EquippedItem(itemId, slotWithItem.Key, item.techType.ToDto()));
}
}
return equipped;
}
}

View File

@@ -0,0 +1,14 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class RadiationMetadataExtractor : EntityMetadataExtractor<RadiationLeak, RadiationMetadata>
{
public override RadiationMetadata Extract(RadiationLeak leak)
{
// Note: this extractor should only be used when this radiation leak is being repaired
float realTimeFix = leak.liveMixin.IsFullHealth() ? (float)Resolve<TimeManager>().RealTimeElapsed : -1;
return new(leak.liveMixin.health, realTimeFix);
}
}

View File

@@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Linq;
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxClient.Unity.Helper;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class RocketMetadataExtractor : EntityMetadataExtractor<Rocket, RocketMetadata>
{
public override RocketMetadata Extract(Rocket rocket)
{
RocketPreflightCheckManager rocketPreflightCheckManager = rocket.RequireComponent<RocketPreflightCheckManager>();
List<int> prechecks = rocketPreflightCheckManager.preflightChecks.Select(i => (int)i).ToList();
return new(rocket.currentRocketStage, DayNightCycle.main.timePassedAsFloat, (int)rocket.elevatorState, rocket.elevatorPosition, prechecks);
}
}

View File

@@ -0,0 +1,18 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
using NitroxModel_Subnautica.DataStructures;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class SeaTreaderMetadataExtractor : EntityMetadataExtractor<SeaTreader, SeaTreaderMetadata>
{
public override SeaTreaderMetadata Extract(SeaTreader seaTreader)
{
if (!DayNightCycle.main)
{
return null;
}
float grazingEndTime = DayNightCycle.main.timePassedAsFloat + seaTreader.grazingTimeLeft;
return new(seaTreader.reverseDirection, grazingEndTime, seaTreader.leashPosition.ToDto());
}
}

View File

@@ -0,0 +1,12 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class SealedDoorMetadataExtractor : EntityMetadataExtractor<Sealed, SealedDoorMetadata>
{
public override SealedDoorMetadata Extract(Sealed entity)
{
return new(entity._sealed, entity.openedAmount);
}
}

View File

@@ -0,0 +1,16 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class SeamothMetadataExtractor : EntityMetadataExtractor<SeaMoth, SeamothMetadata>
{
public override SeamothMetadata Extract(SeaMoth seamoth)
{
bool lightsOn = (seamoth.toggleLights) ? seamoth.toggleLights.GetLightsActive() : true;
LiveMixin liveMixin = seamoth.liveMixin;
SubName subName = seamoth.subName;
return new(lightsOn, liveMixin.health, SubNameInputMetadataExtractor.GetName(subName), SubNameInputMetadataExtractor.GetColors(subName));
}
}

View File

@@ -0,0 +1,27 @@
using System.Linq;
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxClient.Unity.Helper;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
using NitroxModel.DataStructures.Unity;
using NitroxModel_Subnautica.DataStructures;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class SubNameInputMetadataExtractor : EntityMetadataExtractor<SubNameInput, SubNameInputMetadata>
{
public override SubNameInputMetadata Extract(SubNameInput subNameInput)
{
SubName subName = subNameInput.target;
return new(subNameInput.selectedColorIndex, GetName(subName), GetColors(subName));
}
public static string GetName(SubName subName)
{
return subName.AliveOrNull()?.hullName.AliveOrNull()?.text;
}
public static NitroxVector3[] GetColors(SubName subName)
{
return subName.AliveOrNull()?.GetColors().Select(color => color.ToDto()).ToArray();
}
}

View File

@@ -0,0 +1,17 @@
using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
public class WaterParkCreatureMetadataExtractor : EntityMetadataExtractor<WaterParkCreature, WaterParkCreatureMetadata>
{
public override WaterParkCreatureMetadata Extract(WaterParkCreature entity)
{
// We don't need to save metadata for fishes with default values
if (entity.age == -1)
{
return null;
}
return new(entity.age, entity.matureTime, entity.timeNextBreed, entity.bornInside);
}
}