refactor: refactor stuff

This commit is contained in:
2026-08-31 23:12:12 +02:00
parent db0356eb6f
commit ef5f7d127f
28 changed files with 367 additions and 208 deletions
@@ -15,12 +15,12 @@ public sealed class SpawnItemCommand : ICommand
public string Description => "Spawns an item in front of the player."; public string Description => "Spawns an item in front of the player.";
public IReadOnlyList<CommandArgSpec> Args { get; } public IReadOnlyList<CommandArgSpec> Args { get; }
public SpawnItemCommand(IItemSpawner spawner, IItemSpawnPoint spawnPoint) public SpawnItemCommand(IItemSpawner spawner, IItemDefinitionRegistry registry, IItemSpawnPoint spawnPoint)
{ {
_spawner = spawner; _spawner = spawner;
_spawnPoint = spawnPoint; _spawnPoint = spawnPoint;
string[] definitions = spawner.DefinitionIds string[] definitions = registry.DefinitionIds
.Select(id => id.Value) .Select(id => id.Value)
.OrderBy(id => id, StringComparer.Ordinal) .OrderBy(id => id, StringComparer.Ordinal)
.ToArray(); .ToArray();
@@ -11,7 +11,7 @@ public abstract partial class InteractableController : Node3D, IInteractionTarge
public const string InteractionGroup = "interactions"; public const string InteractionGroup = "interactions";
[Export] [Export]
public string Key; public string InstanceKey;
[Export] [Export]
public string PromptText = "Wejdź w interakcję"; public string PromptText = "Wejdź w interakcję";
@@ -51,7 +51,7 @@ public abstract partial class InteractableController : Node3D, IInteractionTarge
public override void _Ready() public override void _Ready()
{ {
InteractionTargetId = InteractionTargetId.From(this.GetStableInstanceIdFrom(Key)); InteractionTargetId = InteractionTargetId.From(this.GetStableInstanceIdFrom(InstanceKey));
AddToGroup(InteractionGroup); AddToGroup(InteractionGroup);
RegisterWithAvailableRegistry(); RegisterWithAvailableRegistry();
} }
@@ -8,7 +8,7 @@ namespace Game.Features.Items.Application;
public sealed class ItemInteractionService( public sealed class ItemInteractionService(
HandsInventory hands, HandsInventory hands,
IItemRepository itemRepository, IItemInstanceRegistry itemRegistry,
IHeldItemPresenter presenter IHeldItemPresenter presenter
) : IItemInteractionService ) : IItemInteractionService
{ {
@@ -20,7 +20,7 @@ public sealed class ItemInteractionService(
public ItemActionResult TryPickup(ItemInstanceId itemId, Hand hand) public ItemActionResult TryPickup(ItemInstanceId itemId, Hand hand)
{ {
if (!itemRepository.TryGet(itemId, out IItemInstance item)) if (!itemRegistry.TryGet(itemId, out IItemInstance item))
{ {
return ItemActionResult.Fail(ItemActionStatus.ItemNotFound, $"Item {itemId} was not found."); return ItemActionResult.Fail(ItemActionStatus.ItemNotFound, $"Item {itemId} was not found.");
} }
@@ -68,7 +68,7 @@ public sealed class ItemInteractionService(
return ItemActionResult.Fail(ItemActionStatus.HandEmpty, "The requested hand is empty."); return ItemActionResult.Fail(ItemActionStatus.HandEmpty, "The requested hand is empty.");
} }
if (!itemRepository.TryGet(itemId.Value, out IItemInstance item)) if (!itemRegistry.TryGet(itemId.Value, out IItemInstance item))
{ {
return ItemActionResult.Fail(ItemActionStatus.ItemNotFound, $"Item {itemId} was not found."); return ItemActionResult.Fail(ItemActionStatus.ItemNotFound, $"Item {itemId} was not found.");
} }
@@ -90,7 +90,7 @@ public sealed class ItemInteractionService(
return ItemActionResult.Fail(ItemActionStatus.HandEmpty, "The requested hand is empty."); return ItemActionResult.Fail(ItemActionStatus.HandEmpty, "The requested hand is empty.");
} }
if (!itemRepository.TryGet(itemId.Value, out IItemInstance item)) if (!itemRegistry.TryGet(itemId.Value, out IItemInstance item))
{ {
return ItemActionResult.Fail(ItemActionStatus.ItemNotFound, $"Item {itemId} was not found."); return ItemActionResult.Fail(ItemActionStatus.ItemNotFound, $"Item {itemId} was not found.");
} }
@@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace Game.Features.Items.Contracts;
public interface IItemDefinitionRegistry
{
IReadOnlyCollection<ItemDefinitionId> DefinitionIds { get; }
bool TryRegister(ItemDefinitionId definitionId, ItemDefinition definition);
bool TryGetDefinition(ItemDefinitionId definitionId, out ItemDefinition definition);
}
@@ -0,0 +1 @@
uid://kjka28plyqeo
@@ -0,0 +1,8 @@
namespace Game.Features.Items.Contracts;
public interface IItemInstanceRegistry
{
void Register(ItemInstanceId itemId, IItemInstance item);
void Unregister(ItemInstanceId itemId);
bool TryGet(ItemInstanceId itemId, out IItemInstance item);
}
@@ -0,0 +1 @@
uid://s83yytevpj52
@@ -1,6 +0,0 @@
namespace Game.Features.Items.Contracts;
public interface IItemRepository
{
bool TryGet(ItemInstanceId itemId, out IItemInstance item);
}
@@ -1 +0,0 @@
uid://e7n1m6v4k9r3q
+5 -3
View File
@@ -1,10 +1,12 @@
using System.Collections.Generic;
using Godot; using Godot;
namespace Game.Features.Items.Contracts; namespace Game.Features.Items.Contracts;
public interface IItemSpawner public interface IItemSpawner
{ {
IReadOnlyCollection<ItemDefinitionId> DefinitionIds { get; } ItemActionResult TrySpawn(
ItemActionResult TrySpawn(ItemDefinitionId definitionId, Vector3 worldPosition, out ItemInstanceId itemId); ItemDefinitionId definitionId,
Vector3 worldPosition,
out ItemInstanceId itemId
);
} }
@@ -5,7 +5,7 @@ using Godot;
namespace Game.Features.Items.Godot.Flashlight; namespace Game.Features.Items.Godot.Flashlight;
public partial class FlashlightItemController : WorldItemController public partial class FlashlightItemController : ItemController
{ {
[Export] public SpotLight3D WorldLight; [Export] public SpotLight3D WorldLight;
@@ -3,15 +3,15 @@ using Godot;
namespace Game.Features.Items.Godot; namespace Game.Features.Items.Godot;
public sealed class PlayerHeldItemPresenter( public sealed class HeldItemPresenter(
ItemWorldController itemWorld, ItemControllerRegistryController itemWorld,
Node3D leftHandAnchor, Node3D leftHandAnchor,
Node3D rightHandAnchor Node3D rightHandAnchor
) : IHeldItemPresenter ) : IHeldItemPresenter
{ {
public bool TryShowHeld(IItemInstance item, Hand hand) public bool TryShowHeld(IItemInstance item, Hand hand)
{ {
if (!itemWorld.TryGetController(item.Info.Id, out WorldItemController controller)) if (!itemWorld.TryGetController(item.Info.Id, out ItemController controller))
{ {
return false; return false;
} }
@@ -23,7 +23,7 @@ public sealed class PlayerHeldItemPresenter(
public bool TryShowDropped(IItemInstance item, Vector3 worldPosition) public bool TryShowDropped(IItemInstance item, Vector3 worldPosition)
{ {
if (!itemWorld.TryGetController(item.Info.Id, out WorldItemController controller)) if (!itemWorld.TryGetController(item.Info.Id, out ItemController controller))
{ {
return false; return false;
} }
@@ -34,7 +34,7 @@ public sealed class PlayerHeldItemPresenter(
public void Refresh(IItemInstance item) public void Refresh(IItemInstance item)
{ {
if (itemWorld.TryGetController(item.Info.Id, out WorldItemController controller)) if (itemWorld.TryGetController(item.Info.Id, out ItemController controller))
{ {
controller.ApplyState(item); controller.ApplyState(item);
} }
@@ -6,17 +6,30 @@ using Godot;
namespace Game.Features.Items.Godot; namespace Game.Features.Items.Godot;
public partial class WorldItemController : RigidBody3D, IPickupTarget public partial class ItemController : RigidBody3D, IPickupTarget
{ {
public const string ItemGroup = "world_items"; public const string ItemGroup = "world_items";
[Export] public string Key; [Export]
[Export] public string DefinitionId = "item"; public string InstanceKey;
[Export] public string DisplayName = "Item";
[Export] public HandRequirement HandRequirement = HandRequirement.One; [Export]
[Export] public CollisionShape3D Collision; public string DefinitionId = "item";
[Export] public Node3D WorldVisual;
[Export] public PackedScene HeldViewScene; [Export]
public string DisplayName = "Item";
[Export]
public HandRequirement HandRequirement = HandRequirement.One;
[Export]
public CollisionShape3D Collision;
[Export]
public Node3D WorldVisual;
[Export]
public PackedScene HeldViewScene;
protected Node3D HeldView { get; private set; } protected Node3D HeldView { get; private set; }
protected bool IsHeld => HeldView is not null; protected bool IsHeld => HeldView is not null;
@@ -25,7 +38,7 @@ public partial class WorldItemController : RigidBody3D, IPickupTarget
public ItemDefinition Definition { get; private set; } public ItemDefinition Definition { get; private set; }
public IItemInstance Item { get; private set; } public IItemInstance Item { get; private set; }
private ItemWorldController _registry; private ItemControllerRegistryController _registry;
public override void _Ready() public override void _Ready()
{ {
@@ -41,16 +54,19 @@ public partial class WorldItemController : RigidBody3D, IPickupTarget
throw new InvalidOperationException($"{Name}: held view scene is not assigned."); throw new InvalidOperationException($"{Name}: held view scene is not assigned.");
} }
Definition = new ItemDefinition(new ItemDefinitionId(DefinitionId), DisplayName, HandRequirement); Definition = new ItemDefinition(
new ItemDefinitionId(DefinitionId),
DisplayName,
HandRequirement
);
ItemInfo = new ItemInfo( ItemInfo = new ItemInfo(
ItemInstanceId.From(this.GetStableInstanceIdFrom(Key)), ItemInstanceId.From(this.GetStableInstanceIdFrom(InstanceKey)),
Definition.Id, Definition.Id,
Definition.DisplayName, Definition.DisplayName,
Definition.HandRequirement Definition.HandRequirement
); );
Item = CreateItemInstance(ItemInfo); Item = CreateItemInstance(ItemInfo);
AddToGroup(ItemGroup); AddToGroup(ItemGroup);
RegisterWithAvailableWorld();
ApplyState(Item); ApplyState(Item);
} }
@@ -59,7 +75,7 @@ public partial class WorldItemController : RigidBody3D, IPickupTarget
_registry?.Unregister(ItemInfo.Id); _registry?.Unregister(ItemInfo.Id);
} }
public void RegisterWith(ItemWorldController registry) public void RegisterWith(ItemControllerRegistryController registry)
{ {
_registry = registry; _registry = registry;
} }
@@ -109,29 +125,12 @@ public partial class WorldItemController : RigidBody3D, IPickupTarget
OnPresentationChanged(item); OnPresentationChanged(item);
} }
protected virtual IItemInstance CreateItemInstance(ItemInfo itemInfo) => new PassiveItemInstance(itemInfo); protected virtual IItemInstance CreateItemInstance(ItemInfo itemInfo) =>
new PassiveItemInstance(itemInfo);
protected virtual void OnHeldViewCreated(Node3D heldView) protected virtual void OnHeldViewCreated(Node3D heldView) { }
{
}
protected virtual void OnHeldViewRemoving(Node3D heldView) protected virtual void OnHeldViewRemoving(Node3D heldView) { }
{
}
protected virtual void OnPresentationChanged(IItemInstance item) protected virtual void OnPresentationChanged(IItemInstance item) { }
{
}
private void RegisterWithAvailableWorld()
{
foreach (Node node in GetTree().GetNodesInGroup(ItemWorldController.RegistryGroup))
{
if (node is ItemWorldController registry)
{
registry.Register(this);
return;
}
}
}
} }
@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using Game.Features.Items.Contracts;
using Godot;
namespace Game.Features.Items.Godot;
public partial class ItemControllerRegistryController : Node
{
private IItemInstanceRegistry _registry;
private readonly Dictionary<ItemInstanceId, ItemController> _items = [];
public IReadOnlyCollection<ItemController> Items => _items.Values;
public void UseRegistry(IItemInstanceRegistry registry)
{
_registry = registry;
}
public void RegisterSceneItems()
{
foreach (Node node in GetTree().GetNodesInGroup(ItemController.ItemGroup))
{
if (node is ItemController item)
{
Register(item);
}
}
}
public void Register(ItemController item)
{
if (_items.TryGetValue(item.ItemInfo.Id, out ItemController existing))
{
if (ReferenceEquals(existing, item))
{
return;
}
throw new InvalidOperationException($"Duplicate item instance id: {item.ItemInfo.Id}.");
}
_items.Add(item.ItemInfo.Id, item);
_registry.Register(item.ItemInfo.Id, item.Item);
item.RegisterWith(this);
}
public void Unregister(ItemInstanceId itemId)
{
_items.Remove(itemId);
_registry.Unregister(itemId);
}
public bool TryGetController(ItemInstanceId itemId, out ItemController controller)
{
return _items.TryGetValue(itemId, out controller);
}
}
@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using Game.Features.Items.Contracts;
using Game.Godot.Extensions;
using Godot;
namespace Game.Features.Items.Godot;
public partial class ItemSceneRegistryController : Node
{
[Export]
public ItemControllerRegistryController ItemWorld;
[Export]
public global::Godot.Collections.Array<PackedScene> ItemScenes = [];
private readonly Dictionary<ItemDefinitionId, PackedScene> _scenes = [];
private IItemDefinitionRegistry _definitions;
public void UseRegistry(IItemDefinitionRegistry registry)
{
_definitions = registry;
}
public override void _Ready()
{
this.AssertRequiredExports();
}
public void Initialize()
{
_scenes.Clear();
foreach (PackedScene scene in ItemScenes)
{
RegisterItem(scene, throwOnDuplicate: true);
}
foreach (ItemController item in ItemWorld.Items)
{
string scenePath = item.SceneFilePath;
if (string.IsNullOrWhiteSpace(scenePath))
{
continue;
}
RegisterItem(ResourceLoader.Load<PackedScene>(scenePath), throwOnDuplicate: false);
}
if (_scenes.Count == 0)
{
throw new InvalidOperationException("The item spawn catalog is empty.");
}
foreach (ItemController item in ItemWorld.Items)
{
ValidateAgainstCatalog(item);
}
}
public bool TryGetScene(ItemDefinitionId definitionId, out PackedScene scene)
{
return _scenes.TryGetValue(definitionId, out scene);
}
private void RegisterItem(PackedScene itemScene, bool throwOnDuplicate)
{
if (itemScene is null)
{
return;
}
ItemController prototype = itemScene.Instantiate<ItemController>();
ItemDefinitionId definitionId = new(prototype.DefinitionId);
ItemDefinition definition = new(
definitionId,
prototype.DisplayName,
prototype.HandRequirement
);
prototype.Free();
if (!_definitions.TryRegister(definitionId, definition))
{
if (throwOnDuplicate)
{
throw new InvalidOperationException(
$"Duplicate item spawn definition: {definitionId}."
);
}
return;
}
_scenes.Add(definitionId, itemScene);
}
private void ValidateAgainstCatalog(ItemController item)
{
if (!_definitions.TryGetDefinition(item.Definition.Id, out ItemDefinition canonical))
{
return;
}
if (!item.Definition.Equals(canonical))
{
throw new InvalidOperationException(
$"{item.Name}: item definition drifted from the catalog for '{item.Definition.Id}'."
);
}
}
}
@@ -0,0 +1 @@
uid://cft7r2j1f0sav
@@ -1,6 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Game.Features.Items.Contracts; using Game.Features.Items.Contracts;
using Game.Godot.Extensions; using Game.Godot.Extensions;
using Godot; using Godot;
@@ -9,57 +6,29 @@ namespace Game.Features.Items.Godot;
public partial class ItemSpawnerController : Node, IItemSpawner public partial class ItemSpawnerController : Node, IItemSpawner
{ {
[Export] public Node3D ItemsRoot; [Export]
[Export] public ItemWorldController ItemWorld; public Node3D ItemsRoot;
[Export] public global::Godot.Collections.Array<PackedScene> ItemScenes = [];
private readonly Dictionary<ItemDefinitionId, PackedScene> _scenes = []; [Export]
public ItemControllerRegistryController ItemWorld;
public IReadOnlyCollection<ItemDefinitionId> DefinitionIds => _scenes.Keys.ToArray(); [Export]
public ItemSceneRegistryController Scenes;
public override void _Ready() public override void _Ready()
{ {
this.AssertRequiredExports(); this.AssertRequiredExports();
} }
public void Initialize()
{
_scenes.Clear();
foreach (PackedScene scene in ItemScenes)
{
RegisterScene(scene);
}
foreach (WorldItemController item in ItemWorld.Items)
{
string scenePath = item.SceneFilePath;
if (string.IsNullOrWhiteSpace(scenePath))
{
continue;
}
PackedScene scene = ResourceLoader.Load<PackedScene>(scenePath);
if (scene is not null)
{
_scenes.TryAdd(item.ItemInfo.DefinitionId, scene);
}
}
if (_scenes.Count == 0)
{
throw new InvalidOperationException("The item spawn catalog is empty.");
}
}
public ItemActionResult TrySpawn( public ItemActionResult TrySpawn(
ItemDefinitionId definitionId, ItemDefinitionId definitionId,
Vector3 worldPosition, Vector3 worldPosition,
out ItemInstanceId itemId out ItemInstanceId itemId
) { )
{
itemId = default; itemId = default;
if (!_scenes.TryGetValue(definitionId, out PackedScene scene)) if (!Scenes.TryGetScene(definitionId, out PackedScene scene))
{ {
return ItemActionResult.Fail( return ItemActionResult.Fail(
ItemActionStatus.UnknownDefinition, ItemActionStatus.UnknownDefinition,
@@ -67,28 +36,11 @@ public partial class ItemSpawnerController : Node, IItemSpawner
); );
} }
WorldItemController item = scene.Instantiate<WorldItemController>(); ItemController item = scene.Instantiate<ItemController>();
ItemsRoot.AddChild(item); ItemsRoot.AddChild(item);
item.GlobalPosition = worldPosition; item.GlobalPosition = worldPosition;
ItemWorld.Register(item); ItemWorld.Register(item);
itemId = item.ItemInfo.Id; itemId = item.ItemInfo.Id;
return ItemActionResult.Ok(); return ItemActionResult.Ok();
} }
private void RegisterScene(PackedScene scene)
{
if (scene is null)
{
return;
}
WorldItemController prototype = scene.Instantiate<WorldItemController>();
ItemDefinitionId definitionId = new(prototype.DefinitionId);
prototype.Free();
if (!_scenes.TryAdd(definitionId, scene))
{
throw new InvalidOperationException($"Duplicate item spawn definition: {definitionId}.");
}
}
} }
@@ -1,69 +0,0 @@
using System;
using System.Collections.Generic;
using Game.Features.Items.Contracts;
using Godot;
namespace Game.Features.Items.Godot;
public partial class ItemWorldController : Node, IItemRepository
{
public const string RegistryGroup = "item_world_registry";
private readonly Dictionary<ItemInstanceId, WorldItemController> _items = [];
public IReadOnlyCollection<WorldItemController> Items => _items.Values;
public override void _EnterTree()
{
AddToGroup(RegistryGroup);
}
public void RegisterSceneItems()
{
foreach (Node node in GetTree().GetNodesInGroup(WorldItemController.ItemGroup))
{
if (node is WorldItemController item)
{
Register(item);
}
}
}
public void Register(WorldItemController item)
{
if (_items.TryGetValue(item.ItemInfo.Id, out WorldItemController existing))
{
if (ReferenceEquals(existing, item))
{
return;
}
throw new InvalidOperationException($"Duplicate item instance id: {item.ItemInfo.Id}.");
}
_items.Add(item.ItemInfo.Id, item);
item.RegisterWith(this);
}
public void Unregister(ItemInstanceId itemId)
{
_items.Remove(itemId);
}
public bool TryGet(ItemInstanceId itemId, out IItemInstance item)
{
if (_items.TryGetValue(itemId, out WorldItemController controller))
{
item = controller.Item;
return true;
}
item = null;
return false;
}
public bool TryGetController(ItemInstanceId itemId, out WorldItemController controller)
{
return _items.TryGetValue(itemId, out controller);
}
}
@@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Linq;
using Game.Features.Items.Contracts;
namespace Game.Features.Items.Infrastructure;
public sealed class ItemDefinitionRegistry : IItemDefinitionRegistry
{
private readonly Dictionary<ItemDefinitionId, ItemDefinition> _definitionsById = [];
public IReadOnlyCollection<ItemDefinitionId> DefinitionIds => _definitionsById.Keys.ToArray();
public bool TryRegister(ItemDefinitionId definitionId, ItemDefinition definition) =>
_definitionsById.TryAdd(definitionId, definition);
public bool TryGetDefinition(ItemDefinitionId definitionId, out ItemDefinition definition) =>
_definitionsById.TryGetValue(definitionId, out definition);
}
@@ -0,0 +1 @@
uid://72g8eg0lvlui
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using Game.Features.Items.Contracts;
namespace Game.Features.Items.Infrastructure;
public sealed class ItemInstanceRegistry : IItemInstanceRegistry
{
private readonly Dictionary<ItemInstanceId, IItemInstance> _itemsById = [];
public void Register(ItemInstanceId itemId, IItemInstance item)
{
if (_itemsById.TryGetValue(itemId, out IItemInstance existing))
{
if (ReferenceEquals(existing, item))
{
return;
}
throw new InvalidOperationException($"Duplicate item instance id: {itemId}.");
}
_itemsById.Add(itemId, item);
}
public void Unregister(ItemInstanceId itemId)
{
_itemsById.Remove(itemId);
}
public bool TryGet(ItemInstanceId itemId, out IItemInstance item) =>
_itemsById.TryGetValue(itemId, out item);
}
@@ -0,0 +1 @@
uid://dfbylp383il2v
@@ -0,0 +1 @@
uid://dj8kxrwjoyfvi
+48 -18
View File
@@ -2,14 +2,15 @@ using System;
using Game.Features.Console.Application.Commands; using Game.Features.Console.Application.Commands;
using Game.Features.Console.Godot; using Game.Features.Console.Godot;
using Game.Features.Console.Infrastructure; using Game.Features.Console.Infrastructure;
using Game.Features.Logging.Godot;
using Game.Features.Logging.Infrastructure;
using Game.Features.Interactions.Application; using Game.Features.Interactions.Application;
using Game.Features.Interactions.Godot; using Game.Features.Interactions.Godot;
using Game.Features.Interactions.Infrastructure; using Game.Features.Interactions.Infrastructure;
using Game.Features.Items.Application; using Game.Features.Items.Application;
using Game.Features.Items.Domain; using Game.Features.Items.Domain;
using Game.Features.Items.Godot; using Game.Features.Items.Godot;
using Game.Features.Items.Infrastructure;
using Game.Features.Logging.Godot;
using Game.Features.Logging.Infrastructure;
using Game.Features.Multiplayer.Godot; using Game.Features.Multiplayer.Godot;
using Game.Features.Players.Contracts; using Game.Features.Players.Contracts;
using Game.Features.Players.Godot; using Game.Features.Players.Godot;
@@ -21,16 +22,33 @@ namespace Game.Godot;
public partial class GameBootstrap : Node public partial class GameBootstrap : Node
{ {
[Export] public ConsoleController Console; [Export]
[Export] public PlayerController Player; public ConsoleController Console;
[Export] public PlayerInteractionController PlayerInteraction;
[Export] public ItemWorldController ItemWorld; [Export]
[Export] public ItemSpawnerController ItemSpawner; public PlayerController Player;
[Export] public InteractionsRegistryController InteractionWorld;
[Export] public MultiplayerController MultiplayerController; [Export]
public PlayerInteractionController PlayerInteraction;
[Export]
public ItemControllerRegistryController ItemWorld;
[Export]
public ItemSceneRegistryController ItemScenes;
[Export]
public ItemSpawnerController ItemSpawner;
[Export]
public InteractionsRegistryController InteractionWorld;
[Export]
public MultiplayerController MultiplayerController;
private FileLogger _fileLogger; private FileLogger _fileLogger;
private CompositeLogger _logger; private CompositeLogger _logger;
private ItemDefinitionRegistry _itemDefinitions;
private void InitializeLogging() private void InitializeLogging()
{ {
@@ -38,7 +56,7 @@ public partial class GameBootstrap : Node
_logger = new CompositeLogger([ _logger = new CompositeLogger([
new GodotConsoleLogger(), new GodotConsoleLogger(),
new LogTargetLogger(Console), new LogTargetLogger(Console),
_fileLogger _fileLogger,
]); ]);
} }
@@ -49,7 +67,7 @@ public partial class GameBootstrap : Node
registry.Register(new EchoCommand()); registry.Register(new EchoCommand());
registry.Register(new ClearCommand()); registry.Register(new ClearCommand());
registry.Register(new NoclipCommand(Player)); registry.Register(new NoclipCommand(Player));
registry.Register(new SpawnItemCommand(ItemSpawner, PlayerInteraction)); registry.Register(new SpawnItemCommand(ItemSpawner, _itemDefinitions, PlayerInteraction));
registry.Register(new HostCommand(MultiplayerController)); registry.Register(new HostCommand(MultiplayerController));
registry.Register(new JoinCommand(MultiplayerController)); registry.Register(new JoinCommand(MultiplayerController));
@@ -59,28 +77,40 @@ public partial class GameBootstrap : Node
private void InitializeMultiplayer() private void InitializeMultiplayer()
{ {
MultiplayerController.UsePeerFactory(new EnetMultiplayerPeerFactory()); MultiplayerController.UsePeerFactory(new EnetMultiplayerPeerFactory());
MultiplayerController.Connected += () => _logger.Info("Multiplayer", "Connected successfully."); MultiplayerController.Connected += () =>
MultiplayerController.ConnectionFailed += () => _logger.Warning("Multiplayer", "Connection failed."); _logger.Info("Multiplayer", "Connected successfully.");
MultiplayerController.Disconnected += () => _logger.Warning("Multiplayer", "Server disconnected."); MultiplayerController.ConnectionFailed += () =>
_logger.Warning("Multiplayer", "Connection failed.");
MultiplayerController.Disconnected += () =>
_logger.Warning("Multiplayer", "Server disconnected.");
MultiplayerController.PeerJoined += id => _logger.Info("Multiplayer", $"Peer {id} joined."); MultiplayerController.PeerJoined += id => _logger.Info("Multiplayer", $"Peer {id} joined.");
MultiplayerController.PeerLeft += id => _logger.Info("Multiplayer", $"Peer {id} left."); MultiplayerController.PeerLeft += id => _logger.Info("Multiplayer", $"Peer {id} left.");
} }
private void InitializeItems() private void InitializeItems()
{ {
ItemInstanceRegistry itemInstanceRegistry = new();
ItemWorld.UseRegistry(itemInstanceRegistry);
ItemWorld.RegisterSceneItems(); ItemWorld.RegisterSceneItems();
ItemSpawner.Initialize();
_itemDefinitions = new ItemDefinitionRegistry();
ItemScenes.UseRegistry(_itemDefinitions);
ItemScenes.Initialize();
InteractionRegistry interactionRegistry = new(); InteractionRegistry interactionRegistry = new();
InteractionWorld.UseRegistry(interactionRegistry); InteractionWorld.UseRegistry(interactionRegistry);
InteractionWorld.RegisterSceneTargets(); InteractionWorld.RegisterSceneTargets();
PlayerHeldItemPresenter presenter = new( HeldItemPresenter presenter = new(
ItemWorld, ItemWorld,
PlayerInteraction.LeftHandAnchor, PlayerInteraction.LeftHandAnchor,
PlayerInteraction.RightHandAnchor PlayerInteraction.RightHandAnchor
); );
ItemInteractionService itemInteractions = new(new HandsInventory(), ItemWorld, presenter); ItemInteractionService itemInteractions = new(
new HandsInventory(),
itemInstanceRegistry,
presenter
);
Interact interactions = new(interactionRegistry); Interact interactions = new(interactionRegistry);
PlayerInteraction.Initialize(new PlayerId(Guid.NewGuid()), itemInteractions, interactions); PlayerInteraction.Initialize(new PlayerId(Guid.NewGuid()), itemInteractions, interactions);
} }
@@ -90,7 +120,7 @@ public partial class GameBootstrap : Node
this.AssertRequiredExports(); this.AssertRequiredExports();
MouseCapture.Capture(); MouseCapture.Capture();
InitializeLogging(); InitializeLogging();
InitializeItems(); InitializeItems();
InitializeMultiplayer(); InitializeMultiplayer();
+11 -4
View File
@@ -6,9 +6,10 @@
[ext_resource type="Script" uid="uid://drctu5y21ey2y" path="res://src/Godot/GameBootstrap.cs" id="3_bootstrap"] [ext_resource type="Script" uid="uid://drctu5y21ey2y" path="res://src/Godot/GameBootstrap.cs" id="3_bootstrap"]
[ext_resource type="PackedScene" uid="uid://bk0771dvonxh8" path="res://src/Features/Console/Godot/ConsoleController.tscn" id="4_console"] [ext_resource type="PackedScene" uid="uid://bk0771dvonxh8" path="res://src/Features/Console/Godot/ConsoleController.tscn" id="4_console"]
[ext_resource type="Script" uid="uid://drqx0wiw0sbj1" path="res://src/Features/Multiplayer/Godot/MultiplayerController.cs" id="5_multiplayer"] [ext_resource type="Script" uid="uid://drqx0wiw0sbj1" path="res://src/Features/Multiplayer/Godot/MultiplayerController.cs" id="5_multiplayer"]
[ext_resource type="Script" uid="uid://f8n3b6w1k5r9m" path="res://src/Features/Items/Godot/ItemWorldController.cs" id="6_items"] [ext_resource type="Script" uid="uid://f8n3b6w1k5r9m" path="res://src/Features/Items/Godot/ItemControllerRegistryController.cs" id="6_items"]
[ext_resource type="PackedScene" uid="uid://cv8m3q1y6r4nf" path="res://src/Features/Items/Godot/Flashlight/FlashlightItem.tscn" id="7_flashlight"] [ext_resource type="PackedScene" uid="uid://cv8m3q1y6r4nf" path="res://src/Features/Items/Godot/Flashlight/FlashlightItem.tscn" id="7_flashlight"]
[ext_resource type="Script" uid="uid://c3x9m6w2q8r1b" path="res://src/Features/Items/Godot/ItemSpawnerController.cs" id="8_spawner"] [ext_resource type="Script" uid="uid://c3x9m6w2q8r1b" path="res://src/Features/Items/Godot/ItemSpawnerController.cs" id="8_spawner"]
[ext_resource type="Script" path="res://src/Features/Items/Godot/ItemSceneRegistryController.cs" id="8b_catalog"]
[ext_resource type="Script" uid="uid://f6q1c9m4w7v2n" path="res://src/Features/Interactions/Godot/InteractionsRegistryController.cs" id="9_interactions"] [ext_resource type="Script" uid="uid://f6q1c9m4w7v2n" path="res://src/Features/Interactions/Godot/InteractionsRegistryController.cs" id="9_interactions"]
[sub_resource type="PanoramaSkyMaterial" id="PanoramaSkyMaterial_epykj"] [sub_resource type="PanoramaSkyMaterial" id="PanoramaSkyMaterial_epykj"]
@@ -22,12 +23,13 @@ background_mode = 2
sky = SubResource("Sky_1kpcd") sky = SubResource("Sky_1kpcd")
glow_enabled = true glow_enabled = true
[node name="GameWorld" type="Node" unique_id=222644234 node_paths=PackedStringArray("Console", "Player", "PlayerInteraction", "ItemWorld", "ItemSpawner", "InteractionWorld", "MultiplayerController")] [node name="GameWorld" type="Node" unique_id=222644234 node_paths=PackedStringArray("Console", "Player", "PlayerInteraction", "ItemWorld", "ItemScenes", "ItemSpawner", "InteractionWorld", "MultiplayerController")]
script = ExtResource("3_bootstrap") script = ExtResource("3_bootstrap")
Console = NodePath("Console") Console = NodePath("Console")
Player = NodePath("Player") Player = NodePath("Player")
PlayerInteraction = NodePath("Player/PlayerInteraction") PlayerInteraction = NodePath("Player/PlayerInteraction")
ItemWorld = NodePath("ItemWorld") ItemWorld = NodePath("ItemWorld")
ItemScenes = NodePath("ItemScenesRegistry")
ItemSpawner = NodePath("ItemSpawner") ItemSpawner = NodePath("ItemSpawner")
InteractionWorld = NodePath("InteractionWorld") InteractionWorld = NodePath("InteractionWorld")
MultiplayerController = NodePath("Multiplayer") MultiplayerController = NodePath("Multiplayer")
@@ -50,11 +52,16 @@ script = ExtResource("5_multiplayer")
[node name="ItemWorld" type="Node" parent="."] [node name="ItemWorld" type="Node" parent="."]
script = ExtResource("6_items") script = ExtResource("6_items")
[node name="ItemSpawner" type="Node" parent="." node_paths=PackedStringArray("ItemsRoot", "ItemWorld")] [node name="ItemScenesRegistry" type="Node" parent="." node_paths=PackedStringArray("ItemWorld")]
script = ExtResource("8b_catalog")
ItemWorld = NodePath("../ItemWorld")
ItemScenes = Array[PackedScene]([ExtResource("7_flashlight")])
[node name="ItemSpawner" type="Node" parent="." node_paths=PackedStringArray("ItemsRoot", "ItemWorld", "Scenes")]
script = ExtResource("8_spawner") script = ExtResource("8_spawner")
ItemsRoot = NodePath("../Entities") ItemsRoot = NodePath("../Entities")
ItemWorld = NodePath("../ItemWorld") ItemWorld = NodePath("../ItemWorld")
ItemScenes = Array[PackedScene]([ExtResource("7_flashlight")]) Scenes = NodePath("../ItemScenesRegistry")
[node name="InteractionWorld" type="Node" parent="."] [node name="InteractionWorld" type="Node" parent="."]
script = ExtResource("9_interactions") script = ExtResource("9_interactions")