diff --git a/README.md b/README.md index 1fde94c..9e3fbbf 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,6 @@ > Make some tanks drive around onchain in MUD + Unity -### [Read the tutorial here →](https://0xpectations.notion.site/Tanks-Tutorial-31fe9c88e4384f7c8f47c09418ee0669) +### Tutorial WIP + +~~[Read the tutorial here →](https://0xpectations.notion.site/Tanks-Tutorial-31fe9c88e4384f7c8f47c09418ee0669)~~ diff --git a/packages/client/Assets/Resources/Tank.prefab b/packages/client/Assets/Resources/Tank.prefab index 0950e83..f968c81 100644 --- a/packages/client/Assets/Resources/Tank.prefab +++ b/packages/client/Assets/Resources/Tank.prefab @@ -662,6 +662,7 @@ GameObject: - component: {fileID: 8494640119540737440} - component: {fileID: 436748528113392774} - component: {fileID: 4326114591028432108} + - component: {fileID: 7416213480386566023} m_Layer: 6 m_Name: Tank m_TagString: Untagged @@ -683,9 +684,8 @@ Transform: m_Children: - {fileID: 5863183790166139544} - {fileID: 2819717444226315097} - - {fileID: 391825217966097094} m_Father: {fileID: 0} - m_RootOrder: 4 + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!54 &2428493033774599304 Rigidbody: @@ -767,6 +767,20 @@ MonoBehaviour: m_ZeroHealthColor: {r: 1, g: 0, b: 0, a: 1} m_ExplosionPrefab: {fileID: 151086, guid: edb45b5f1585b480cb512c431287720f, type: 3} shell: {fileID: 7329492745129491182, guid: 09273cb84b31b45ebab13ee0a7a982bc, type: 3} + _player: {fileID: 0} +--- !u!114 &7416213480386566023 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7715415014581269739} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 96d6b0080d20419ba009908f725934a3, type: 3} + m_Name: + m_EditorClassIdentifier: + key: --- !u!1 &8570941342493416859 GameObject: m_ObjectHideFlags: 0 @@ -851,102 +865,6 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &8658281875438534592 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 391825217966097094} - - component: {fileID: 671135761367330302} - - component: {fileID: 5295917751157974073} - - component: {fileID: 3926139412989139538} - m_Layer: 0 - m_Name: Targeting - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &391825217966097094 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8658281875438534592} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: -0, z: 0} - m_LocalScale: {x: 3, y: 3, z: 3} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 3751897668752642862} - m_RootOrder: -1 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &671135761367330302 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8658281875438534592} - m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &5295917751157974073 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8658281875438534592} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: fc616bbb8f63840cbaa85d9dd5993259, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &3926139412989139538 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8658281875438534592} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: a0bd1db452f064f42882b468570345e8, type: 3} - m_Name: - m_EditorClassIdentifier: --- !u!1 &9211904990276239997 GameObject: m_ObjectHideFlags: 0 diff --git a/packages/client/Assets/Resources/latest.json b/packages/client/Assets/Resources/latest.json index d027302..5bcaa14 100644 --- a/packages/client/Assets/Resources/latest.json +++ b/packages/client/Assets/Resources/latest.json @@ -1,4 +1,4 @@ { "worldAddress": "0x5FbDB2315678afecb367f032d93F642f64180aa3", - "blockNumber": 435 + "blockNumber": 11 } \ No newline at end of file diff --git a/packages/client/Assets/Scenes/Main.unity b/packages/client/Assets/Scenes/Main.unity index b720c6f..5a070ff 100644 --- a/packages/client/Assets/Scenes/Main.unity +++ b/packages/client/Assets/Scenes/Main.unity @@ -226,6 +226,50 @@ PrefabInstance: insertIndex: -1 addedObject: {fileID: 1490863721} m_SourcePrefab: {fileID: 100100000, guid: f6fdbca0fb0f8499abbf773fb8b95662, type: 3} +--- !u!1 &490201550 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 490201552} + - component: {fileID: 490201551} + m_Layer: 0 + m_Name: HealthManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &490201551 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 490201550} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 632fc88e05af411286f9b52741d60fe6, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &490201552 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 490201550} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 6.6326003, y: 5.6776605, z: 6.367436} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1001 &518603701 PrefabInstance: m_ObjectHideFlags: 0 @@ -711,6 +755,38 @@ MonoBehaviour: mipBias: 0 varianceClampScale: 0.9 contrastAdaptiveSharpening: 0 +--- !u!1 &1038255101 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1038255102} + m_Layer: 0 + m_Name: AttackManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1038255102 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1038255101} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 6.6326003, y: 5.6776605, z: 6.367436} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2098282585} + m_Father: {fileID: 0} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1095405278 GameObject: m_ObjectHideFlags: 0 @@ -851,6 +927,51 @@ BoxCollider: serializedVersion: 3 m_Size: {x: 1.0000001, y: 2.9999998, z: 2.0000002} m_Center: {x: 0, y: 1.4999999, z: 0.49999815} +--- !u!1 &1163357622 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1163357624} + - component: {fileID: 1163357623} + m_Layer: 0 + m_Name: PlayerManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1163357623 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1163357622} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f110820a9e29b41449001a35dfb154d2, type: 3} + m_Name: + m_EditorClassIdentifier: + playerPrefab: {fileID: 7715415014581269739, guid: 34b7f8ce8560f4ee581e518d0313faea, type: 3} +--- !u!4 &1163357624 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1163357622} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 6.6326003, y: 5.6776605, z: 6.367436} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1001 &1184501158 PrefabInstance: m_ObjectHideFlags: 0 @@ -1109,6 +1230,57 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1290505662 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1290505664} + - component: {fileID: 1290505663} + m_Layer: 0 + m_Name: NetworkManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1290505663 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1290505662} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 88c2f0f44d7cc4c3abe5f498cfeca6be, type: 3} + m_Name: + m_EditorClassIdentifier: + jsonRpcUrl: http://localhost:8545 + wsRpcUrl: ws://localhost:8545 + chainId: 31337 + contractAddress: + pk: + uniqueWallets: 0 + disableCache: 1 +--- !u!4 &1290505664 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1290505662} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 6.6326003, y: 5.6776605, z: 6.367436} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1293011545 stripped GameObject: m_CorrespondingSourceObject: {fileID: 919132149155446097, guid: a4c374c4057eb498889c28c867199b24, type: 3} @@ -1514,3 +1686,121 @@ PrefabInstance: insertIndex: -1 addedObject: {fileID: 1293011549} m_SourcePrefab: {fileID: 100100000, guid: a4c374c4057eb498889c28c867199b24, type: 3} +--- !u!1 &2098282584 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2098282585} + - component: {fileID: 2098282589} + - component: {fileID: 2098282588} + - component: {fileID: 2098282587} + - component: {fileID: 2098282586} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2098282585 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2098282584} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: -5, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1038255102} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &2098282586 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2098282584} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a0bd1db452f064f42882b468570345e8, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!135 &2098282587 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2098282584} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &2098282588 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2098282584} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: fc616bbb8f63840cbaa85d9dd5993259, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &2098282589 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2098282584} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} diff --git a/packages/client/Assets/Scripts/HealthManager.cs b/packages/client/Assets/Scripts/HealthManager.cs new file mode 100644 index 0000000..ee4cf4b --- /dev/null +++ b/packages/client/Assets/Scripts/HealthManager.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using UniRx; +using mud.Client; +using mud.Network.schemas; +using mud.Unity; +using UnityEngine; +using ObservableExtensions = UniRx.ObservableExtensions; + +public class HealthManager : MonoBehaviour +{ + private CompositeDisposable _disposable = new(); + private NetworkManager net; + + void Start() + { + net = NetworkManager.Instance; + net.OnNetworkInitialized += SubscribeHealth; + } + + void SubscribeHealth(NetworkManager nm) + { + var healthTable = new TableId("", "Health"); + var healthUpdated = new Query().In(healthTable); + var sub = ObservableExtensions.Subscribe(net.ds.RxQuery(healthUpdated).ObserveOnMainThread(), OnHealthChange); + _disposable.Add(sub); + } + + // TODO: Callback for HealthTable update + private void OnHealthChange((List SetRecords, List RemovedRecords) update) + { + foreach (var setRecord in update.SetRecords) + { + var tankHealth = FindTankHealthByKey(setRecord.key); + if (tankHealth == null) continue; + + var currentValue = Convert.ToSingle(setRecord.value["value"]); + tankHealth.SetHealth(currentValue); + } + + foreach (var removedRecord in update.RemovedRecords) + { + var tankHealth = FindTankHealthByKey(removedRecord.key); + tankHealth?.OnDeath(); + } + } + + private TankHealth FindTankHealthByKey(string key) + { + // If there are many entities in the scene, it might be better to store the key-TankHealth pairs + // in a Dictionary to improve lookup speed. + foreach (var tankHealth in FindObjectsOfType()) + { + if (tankHealth._player.key == key) + { + return tankHealth; + } + } + + return null; + } + + private void OnDestroy() + { + _disposable?.Dispose(); + } +} diff --git a/packages/client/Assets/Scripts/HealthManager.cs.meta b/packages/client/Assets/Scripts/HealthManager.cs.meta new file mode 100644 index 0000000..04a3580 --- /dev/null +++ b/packages/client/Assets/Scripts/HealthManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 632fc88e05af411286f9b52741d60fe6 +timeCreated: 1690238207 \ No newline at end of file diff --git a/packages/client/Assets/Scripts/IWorld/ContractDefinition/IWorldDefinition.cs b/packages/client/Assets/Scripts/IWorld/ContractDefinition/IWorldDefinition.cs index 77b77ea..10fb3b2 100644 --- a/packages/client/Assets/Scripts/IWorld/ContractDefinition/IWorldDefinition.cs +++ b/packages/client/Assets/Scripts/IWorld/ContractDefinition/IWorldDefinition.cs @@ -205,14 +205,6 @@ public class GrantAccessFunctionBase : FunctionMessage public virtual string Grantee { get; set; } } - public partial class IncrementFunction : IncrementFunctionBase { } - - [Function("increment", "uint32")] - public class IncrementFunctionBase : FunctionMessage - { - - } - public partial class InstallModuleFunction : InstallModuleFunctionBase { } [Function("installModule")] @@ -922,8 +914,6 @@ public class GetSchemaOutputDTOBase : IFunctionOutputDTO - - diff --git a/packages/client/Assets/Scripts/IWorld/Service/IWorldService.cs b/packages/client/Assets/Scripts/IWorld/Service/IWorldService.cs index 8bcb315..c622305 100644 --- a/packages/client/Assets/Scripts/IWorld/Service/IWorldService.cs +++ b/packages/client/Assets/Scripts/IWorld/Service/IWorldService.cs @@ -367,26 +367,6 @@ public Task GrantAccessRequestAndWaitForReceiptAsync(byte[] return ContractHandler.SendRequestAndWaitForReceiptAsync(grantAccessFunction, cancellationToken); } - public Task IncrementRequestAsync(IncrementFunction incrementFunction) - { - return ContractHandler.SendRequestAsync(incrementFunction); - } - - public Task IncrementRequestAsync() - { - return ContractHandler.SendRequestAsync(); - } - - public Task IncrementRequestAndWaitForReceiptAsync(IncrementFunction incrementFunction, CancellationTokenSource cancellationToken = null) - { - return ContractHandler.SendRequestAndWaitForReceiptAsync(incrementFunction, cancellationToken); - } - - public Task IncrementRequestAndWaitForReceiptAsync(CancellationTokenSource cancellationToken = null) - { - return ContractHandler.SendRequestAndWaitForReceiptAsync(null, cancellationToken); - } - public Task InstallModuleRequestAsync(InstallModuleFunction installModuleFunction) { return ContractHandler.SendRequestAsync(installModuleFunction); diff --git a/packages/client/Assets/Scripts/PlayerController.cs b/packages/client/Assets/Scripts/PlayerController.cs index 75d18ed..2c47f88 100644 --- a/packages/client/Assets/Scripts/PlayerController.cs +++ b/packages/client/Assets/Scripts/PlayerController.cs @@ -5,6 +5,7 @@ using DefaultNamespace; using IWorld.ContractDefinition; using mud.Client; +using mud.Network.schemas; using mud.Unity; using UniRx; using UnityEngine; @@ -12,111 +13,117 @@ public class PlayerController : MonoBehaviour { - private Camera _camera; - private Vector3? _destination; - - public GameObject destinationMarker; - private GameObject _destinationMarker; - - private bool _hasDestination; - private IDisposable? _disposer; - private TankShooting _target; - // TODO: Get PlayerSync component - // TODO: Get NetworkManager - - void Start() - { - _camera = Camera.main; - var target = FindObjectOfType(); - if (target == null) return; - _target = target; - - // TODO: Get NetworkManager - - // TODO: Get player sync - - // TODO: Subscribe to Position table - } - - // TODO: Callback for Position table update - // private void OnChainPositionUpdate(PositionTableUpdate update) - // { - // if (_player.key == null || update.Key != _player.key) return; - // if (_player.IsLocalPlayer()) return; - // var currentValue = update.TypedValue.Item1; - // if (currentValue == null) return; - // var x = Convert.ToSingle(currentValue.x); - // var y = Convert.ToSingle(currentValue.y); - // _destination = new Vector3(x, 0, y); - // } - - - // TODO: Send tx - private async UniTaskVoid SendMoveTxAsync(int x, int y) - { - try - { - // TODO: Send tx from NetworkManager - } - catch (Exception ex) - { - // Handle your exception here - Debug.LogException(ex); - } - } - - void Update() - { - var pos = transform.position; - if (_destination.HasValue && Vector3.Distance(pos, _destination.Value) < 0.5) - { - _destination = null; - if (_destinationMarker != null) - { - Destroy(_destinationMarker); - } - } - else - { - if (_destination != null) - { - var newPosition = Vector3.Lerp(transform.position, _destination.Value, Time.deltaTime); - var currentTransform = transform; - currentTransform.position = newPosition; - - // Determine the new rotation - var lookRotation = Quaternion.LookRotation(_destination.Value - currentTransform.position); - var newRotation = Quaternion.Lerp(transform.rotation, lookRotation, Time.deltaTime); - transform.rotation = newRotation; - } - } - - // TODO: Early return if not local player - if (_target.RangeVisible) return; - if (Input.GetMouseButtonDown(0)) - { - var ray = _camera.ScreenPointToRay(Input.mousePosition); - if (!Physics.Raycast(ray, out var hit)) return; - if (hit.collider.name != "floor-large") return; - - var dest = hit.point; - dest.x = Mathf.Floor(dest.x); - dest.y = Mathf.Floor(dest.y); - dest.z = Mathf.Floor(dest.z); - _destination = dest; - - if (_destinationMarker != null) - { - Destroy(_destinationMarker); - } - - _destinationMarker = Instantiate(destinationMarker, dest, Quaternion.identity); - // TODO: Send Tx - } - } - - private void OnDestroy() - { - _disposer?.Dispose(); - } + private Camera _camera; + private Vector3? _destination; + + public GameObject destinationMarker; + private GameObject _destinationMarker; + + private bool _hasDestination; + private IDisposable? _disposer; + private TankShooting _target; + private PlayerSync _player; + + void Start() + { + _camera = Camera.main; + var target = FindObjectOfType(); + if (target == null) return; + _target = target; + + var ds = NetworkManager.Instance.ds; + + _player = GetComponent(); + + var positionTable = new TableId("", "Position"); + var query = new Query().In(positionTable); + var sub = ds.RxQuery(query); + _disposer = ObservableExtensions.Subscribe(sub.ObserveOnMainThread(), OnChainPositionUpdate); + } + + private void OnChainPositionUpdate((List SetRecords, List RemovedRecords) update) + { + if (_player.key == null) return; + if (_player.IsLocalPlayer()) return; + foreach (var setRecord in update.SetRecords) + { + if (setRecord.key != _player.key) continue; + var currentValue = setRecord.value; + if (currentValue == null) continue; + var x = Convert.ToSingle(currentValue["x"]); + var y = Convert.ToSingle(currentValue["y"]); + _destination = new Vector3(x, 0, y); + } + } + + + // TODO: Send tx + private async UniTaskVoid SendMoveTxAsync(int x, int y) + { + try + { + await NetworkManager.Instance.worldSend.TxExecute(x, y); + } + catch (Exception ex) + { + // Handle your exception here + Debug.LogException(ex); + } + } + + void Update() + { + var pos = transform.position; + if (_destination.HasValue && Vector3.Distance(pos, _destination.Value) < 0.5) + { + _destination = null; + if (_destinationMarker != null) + { + Destroy(_destinationMarker); + } + } + else + { + if (_destination != null) + { + var newPosition = Vector3.Lerp(transform.position, _destination.Value, Time.deltaTime); + var currentTransform = transform; + currentTransform.position = newPosition; + + // Determine the new rotation + var lookRotation = Quaternion.LookRotation(_destination.Value - currentTransform.position); + var newRotation = Quaternion.Lerp(transform.rotation, lookRotation, Time.deltaTime); + transform.rotation = newRotation; + } + } + + // TODO: Early return if not local player + if (!_player.IsLocalPlayer() || _target.RangeVisible) return; + if (Input.GetMouseButtonDown(0)) + { + + var ray = _camera.ScreenPointToRay(Input.mousePosition); + if (!Physics.Raycast(ray, out var hit)) return; + if (hit.collider.name != "floor-large") return; + + var dest = hit.point; + dest.x = Mathf.Floor(dest.x); + dest.y = Mathf.Floor(dest.y); + dest.z = Mathf.Floor(dest.z); + _destination = dest; + + if (_destinationMarker != null) + { + Destroy(_destinationMarker); + } + + _destinationMarker = Instantiate(destinationMarker, dest, Quaternion.identity); + SendMoveTxAsync(Convert.ToInt32(dest.x), Convert.ToInt32(dest.z)).Forget(); + } + } + + private void OnDestroy() + { + _disposer?.Dispose(); + } } diff --git a/packages/client/Assets/Scripts/PlayerManager.cs b/packages/client/Assets/Scripts/PlayerManager.cs index f1ceb3f..8c506f0 100644 --- a/packages/client/Assets/Scripts/PlayerManager.cs +++ b/packages/client/Assets/Scripts/PlayerManager.cs @@ -1,8 +1,10 @@ +using System; +using System.Collections.Generic; using DefaultNamespace; using IWorld.ContractDefinition; using mud.Client; +using mud.Network.schemas; using mud.Unity; -using Newtonsoft.Json; using UniRx; using UnityEngine; using ObservableExtensions = UniRx.ObservableExtensions; @@ -22,16 +24,39 @@ void Start() async void Spawn(NetworkManager nm) { - // TODO: Check if current player exists in PlayerTable - // TODO: If not, make the Spawn Tx + var addressKey = net.addressKey; + var playerTable = new TableId("", "Player"); + var currentPlayer = net.ds.GetValue(playerTable, addressKey); + if (currentPlayer == null) + { + await nm.worldSend.TxExecute(0, 0); + } - // TODO: Subscribe to PlayerTable + var playerQuery = new Query().In(playerTable); + var playerSub = ObservableExtensions.Subscribe(net.ds.RxQuery(playerQuery).ObserveOnMainThread(), OnUpdatePlayers); + _disposers.Add(playerSub); } // TODO: Callback for PlayerTable update - // private void OnUpdatePlayers(PlayerTableUpdate update) - // { - // } + private void OnUpdatePlayers((List SetRecords, List RemovedRecords) update) + { + foreach (var setRecord in update.SetRecords) + { + var currentValue = setRecord.value; + if (currentValue == null) continue; + var positionTable = new TableId("", "Position"); + var playerPosition = net.ds.GetValue(positionTable, setRecord.key); + if (playerPosition == null) continue; + var playerSpawnPoint = new Vector3(Convert.ToSingle(playerPosition.value["x"]), 0, Convert.ToSingle(playerPosition.value["y"])); + var player = Instantiate(playerPrefab, playerSpawnPoint, Quaternion.identity); + // add to CameraControl's Targets array + var cameraControl = GameObject.Find("CameraRig").GetComponent(); + cameraControl.m_Targets.Add(player.transform); + player.GetComponent().key = setRecord.key; + if (setRecord.key != net.addressKey) continue; + PlayerSync.localPlayerKey = setRecord.key; + } + } private void OnDestroy() { diff --git a/packages/client/Assets/Scripts/TankHealth.cs b/packages/client/Assets/Scripts/TankHealth.cs index 6441f45..6c37e3c 100644 --- a/packages/client/Assets/Scripts/TankHealth.cs +++ b/packages/client/Assets/Scripts/TankHealth.cs @@ -1,6 +1,10 @@ using System; +using System.Collections.Generic; using UniRx; using DefaultNamespace; +using mud.Client; +using mud.Network.schemas; +using mud.Unity; using Newtonsoft.Json; using UnityEngine; using UnityEngine.UI; @@ -17,21 +21,19 @@ public class TankHealth : MonoBehaviour public Color m_ZeroHealthColor = Color.red; public GameObject m_ExplosionPrefab; public GameObject shell; - - // TODO: Get PlayerSync - + public PlayerSync _player; private ParticleSystem m_ExplosionParticles; private bool m_Dead; private CompositeDisposable _disposable = new(); + private NetworkManager net; + private void Awake() { m_ExplosionParticles = Instantiate(m_ExplosionPrefab).GetComponent(); m_ExplosionParticles.gameObject.SetActive(false); - // TODO: Get PlayerSync component - // TODO: Subscribe to HealthTable Updates - // TODO: Subscribe to HealthTable Deletions + _player = GetComponent(); } @@ -42,16 +44,15 @@ private void OnEnable() SetHealthUI(); } - - // TODO: Callback for HealthTable update - // private void OnHealthChange(HealthTableUpdate update) - // { - // } - - // TODO: Callback for HealthTable deletion - // private void OnPlayerDeath(HealthTableUpdate update) - // { - // } + + public void SetHealth(float health) + { + m_CurrentHealth = health; + var initialShellPosition = transform.position; + initialShellPosition.y += 10; + Instantiate(shell, initialShellPosition, Quaternion.LookRotation(Vector3.down)); + SetHealthUI(); + } private void SetHealthUI() { @@ -60,7 +61,7 @@ private void SetHealthUI() m_FillImage.color = Color.Lerp(m_ZeroHealthColor, m_FullHealthColor, m_CurrentHealth / m_StartingHealth); } - private void OnDeath() + public void OnDeath() { m_Dead = true; m_ExplosionParticles.transform.position = transform.position; diff --git a/packages/client/Assets/Scripts/TankShooting.cs b/packages/client/Assets/Scripts/TankShooting.cs index 054488e..544073b 100644 --- a/packages/client/Assets/Scripts/TankShooting.cs +++ b/packages/client/Assets/Scripts/TankShooting.cs @@ -26,7 +26,7 @@ private async UniTaskVoid SendFireTxAsync(int x, int y) { try { - // TODO: Send tx from NetworkManager + await NetworkManager.Instance.worldSend.TxExecute(x, y); } catch (Exception ex) { @@ -57,7 +57,7 @@ void Update() if (Input.GetMouseButtonDown(0) && !_fired) { _fired = true; - // TODO: Send Tx + SendFireTxAsync(Convert.ToInt32(dest.x), Convert.ToInt32(dest.z)).Forget(); _fired = false; } } diff --git a/packages/client/Assets/Scripts/codegen/CounterTable.cs b/packages/client/Assets/Scripts/codegen/DamageTable.cs similarity index 61% rename from packages/client/Assets/Scripts/codegen/CounterTable.cs rename to packages/client/Assets/Scripts/codegen/DamageTable.cs index 430cee6..e79e978 100644 --- a/packages/client/Assets/Scripts/codegen/CounterTable.cs +++ b/packages/client/Assets/Scripts/codegen/DamageTable.cs @@ -10,42 +10,15 @@ namespace DefaultNamespace { - public class CounterTableUpdate : TypedRecordUpdate> { } + public class DamageTableUpdate : TypedRecordUpdate> { } - public class CounterTable : IMudTable + public class DamageTable : IMudTable { - public static readonly TableId TableId = new("", "Counter"); + public static readonly TableId TableId = new("", "Damage"); public ulong? value; - public static CounterTable? GetTableValue(string key) - { - var query = new Query() - .Find("?value", "?attribute") - .Where(TableId.ToString(), key, "?attribute", "?value"); - var result = NetworkManager.Instance.ds.Query(query); - var counterTable = new CounterTable(); - var hasValues = false; - - foreach (var record in result) - { - var attribute = record["attribute"].ToString(); - var value = record["value"]; - - switch (attribute) - { - case "value": - var valueValue = (ulong)value; - counterTable.value = valueValue; - hasValues = true; - break; - } - } - - return hasValues ? counterTable : null; - } - - public static IObservable OnRecordUpdate() + public static IObservable OnRecordUpdate() { return NetworkManager.Instance.ds.OnDataStoreUpdate .Where( @@ -54,7 +27,7 @@ public static IObservable OnRecordUpdate() ) .Select( update => - new CounterTableUpdate + new DamageTableUpdate { TableId = update.TableId, Key = update.Key, @@ -64,7 +37,7 @@ public static IObservable OnRecordUpdate() ); } - public static IObservable OnRecordInsert() + public static IObservable OnRecordInsert() { return NetworkManager.Instance.ds.OnDataStoreUpdate .Where( @@ -73,7 +46,7 @@ public static IObservable OnRecordInsert() ) .Select( update => - new CounterTableUpdate + new DamageTableUpdate { TableId = update.TableId, Key = update.Key, @@ -83,7 +56,7 @@ public static IObservable OnRecordInsert() ); } - public static IObservable OnRecordDelete() + public static IObservable OnRecordDelete() { return NetworkManager.Instance.ds.OnDataStoreUpdate .Where( @@ -93,7 +66,7 @@ public static IObservable OnRecordDelete() ) .Select( update => - new CounterTableUpdate + new DamageTableUpdate { TableId = update.TableId, Key = update.Key, @@ -103,18 +76,18 @@ public static IObservable OnRecordDelete() ); } - public static Tuple MapUpdates( + public static Tuple MapUpdates( Tuple value ) { - CounterTable? current = null; - CounterTable? previous = null; + DamageTable? current = null; + DamageTable? previous = null; if (value.Item1 != null) { try { - current = new CounterTable + current = new DamageTable { value = value.Item1.TryGetValue("value", out var valueVal) ? (ulong)valueVal @@ -123,7 +96,7 @@ public static IObservable OnRecordDelete() } catch (InvalidCastException) { - current = new CounterTable { value = null, }; + current = new DamageTable { value = null, }; } } @@ -131,7 +104,7 @@ public static IObservable OnRecordDelete() { try { - previous = new CounterTable + previous = new DamageTable { value = value.Item2.TryGetValue("value", out var valueVal) ? (ulong)valueVal @@ -140,11 +113,11 @@ public static IObservable OnRecordDelete() } catch (InvalidCastException) { - previous = new CounterTable { value = null, }; + previous = new DamageTable { value = null, }; } } - return new Tuple(current, previous); + return new Tuple(current, previous); } } } diff --git a/packages/client/Assets/Scripts/codegen/CounterTable.cs.meta b/packages/client/Assets/Scripts/codegen/DamageTable.cs.meta similarity index 83% rename from packages/client/Assets/Scripts/codegen/CounterTable.cs.meta rename to packages/client/Assets/Scripts/codegen/DamageTable.cs.meta index 47f54e2..453fb9b 100644 --- a/packages/client/Assets/Scripts/codegen/CounterTable.cs.meta +++ b/packages/client/Assets/Scripts/codegen/DamageTable.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: fdea79aea56b3408db901da92d2b4037 +guid: cab97f41b1a014f949bca94fb06d73a6 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/packages/client/Assets/Scripts/codegen/HealthTable.cs b/packages/client/Assets/Scripts/codegen/HealthTable.cs new file mode 100644 index 0000000..4696412 --- /dev/null +++ b/packages/client/Assets/Scripts/codegen/HealthTable.cs @@ -0,0 +1,123 @@ +/* Autogenerated file. Manual edits will not be saved.*/ + +#nullable enable +using System; +using mud.Client; +using mud.Network.schemas; +using mud.Unity; +using UniRx; +using Property = System.Collections.Generic.Dictionary; + +namespace DefaultNamespace +{ + public class HealthTableUpdate : TypedRecordUpdate> { } + + public class HealthTable : IMudTable + { + public static readonly TableId TableId = new("", "Health"); + + public ulong? value; + + public static IObservable OnRecordUpdate() + { + return NetworkManager.Instance.ds.OnDataStoreUpdate + .Where( + update => + update.TableId == TableId.ToString() && update.Type == UpdateType.SetField + ) + .Select( + update => + new HealthTableUpdate + { + TableId = update.TableId, + Key = update.Key, + Value = update.Value, + TypedValue = MapUpdates(update.Value) + } + ); + } + + public static IObservable OnRecordInsert() + { + return NetworkManager.Instance.ds.OnDataStoreUpdate + .Where( + update => + update.TableId == TableId.ToString() && update.Type == UpdateType.SetRecord + ) + .Select( + update => + new HealthTableUpdate + { + TableId = update.TableId, + Key = update.Key, + Value = update.Value, + TypedValue = MapUpdates(update.Value) + } + ); + } + + public static IObservable OnRecordDelete() + { + return NetworkManager.Instance.ds.OnDataStoreUpdate + .Where( + update => + update.TableId == TableId.ToString() + && update.Type == UpdateType.DeleteRecord + ) + .Select( + update => + new HealthTableUpdate + { + TableId = update.TableId, + Key = update.Key, + Value = update.Value, + TypedValue = MapUpdates(update.Value) + } + ); + } + + public static Tuple MapUpdates( + Tuple value + ) + { + HealthTable? current = null; + HealthTable? previous = null; + + if (value.Item1 != null) + { + try + { + current = new HealthTable + { + value = value.Item1.TryGetValue("value", out var valueVal) + ? (ulong)valueVal + : default, + }; + } + catch (InvalidCastException) + { + current = new HealthTable { value = null, }; + } + } + + if (value.Item2 != null) + { + try + { + previous = new HealthTable + { + value = value.Item2.TryGetValue("value", out var valueVal) + ? (ulong)valueVal + : default, + }; + } + catch (InvalidCastException) + { + previous = new HealthTable { value = null, }; + } + } + + return new Tuple(current, previous); + } + } +} diff --git a/packages/client/Assets/Scripts/codegen/HealthTable.cs.meta b/packages/client/Assets/Scripts/codegen/HealthTable.cs.meta new file mode 100644 index 0000000..abdbbe3 --- /dev/null +++ b/packages/client/Assets/Scripts/codegen/HealthTable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f5485b198b7db49c78029c4445ddf608 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/client/Assets/Scripts/codegen/PlayerTable.cs b/packages/client/Assets/Scripts/codegen/PlayerTable.cs new file mode 100644 index 0000000..dea54ef --- /dev/null +++ b/packages/client/Assets/Scripts/codegen/PlayerTable.cs @@ -0,0 +1,123 @@ +/* Autogenerated file. Manual edits will not be saved.*/ + +#nullable enable +using System; +using mud.Client; +using mud.Network.schemas; +using mud.Unity; +using UniRx; +using Property = System.Collections.Generic.Dictionary; + +namespace DefaultNamespace +{ + public class PlayerTableUpdate : TypedRecordUpdate> { } + + public class PlayerTable : IMudTable + { + public static readonly TableId TableId = new("", "Player"); + + public bool? value; + + public static IObservable OnRecordUpdate() + { + return NetworkManager.Instance.ds.OnDataStoreUpdate + .Where( + update => + update.TableId == TableId.ToString() && update.Type == UpdateType.SetField + ) + .Select( + update => + new PlayerTableUpdate + { + TableId = update.TableId, + Key = update.Key, + Value = update.Value, + TypedValue = MapUpdates(update.Value) + } + ); + } + + public static IObservable OnRecordInsert() + { + return NetworkManager.Instance.ds.OnDataStoreUpdate + .Where( + update => + update.TableId == TableId.ToString() && update.Type == UpdateType.SetRecord + ) + .Select( + update => + new PlayerTableUpdate + { + TableId = update.TableId, + Key = update.Key, + Value = update.Value, + TypedValue = MapUpdates(update.Value) + } + ); + } + + public static IObservable OnRecordDelete() + { + return NetworkManager.Instance.ds.OnDataStoreUpdate + .Where( + update => + update.TableId == TableId.ToString() + && update.Type == UpdateType.DeleteRecord + ) + .Select( + update => + new PlayerTableUpdate + { + TableId = update.TableId, + Key = update.Key, + Value = update.Value, + TypedValue = MapUpdates(update.Value) + } + ); + } + + public static Tuple MapUpdates( + Tuple value + ) + { + PlayerTable? current = null; + PlayerTable? previous = null; + + if (value.Item1 != null) + { + try + { + current = new PlayerTable + { + value = value.Item1.TryGetValue("value", out var valueVal) + ? (bool)valueVal + : default, + }; + } + catch (InvalidCastException) + { + current = new PlayerTable { value = null, }; + } + } + + if (value.Item2 != null) + { + try + { + previous = new PlayerTable + { + value = value.Item2.TryGetValue("value", out var valueVal) + ? (bool)valueVal + : default, + }; + } + catch (InvalidCastException) + { + previous = new PlayerTable { value = null, }; + } + } + + return new Tuple(current, previous); + } + } +} diff --git a/packages/client/Assets/Scripts/codegen/PlayerTable.cs.meta b/packages/client/Assets/Scripts/codegen/PlayerTable.cs.meta new file mode 100644 index 0000000..a076d7e --- /dev/null +++ b/packages/client/Assets/Scripts/codegen/PlayerTable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: db614818e7d744fb79ba6076ba530979 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/client/Assets/Scripts/codegen/PositionTable.cs b/packages/client/Assets/Scripts/codegen/PositionTable.cs new file mode 100644 index 0000000..d645e70 --- /dev/null +++ b/packages/client/Assets/Scripts/codegen/PositionTable.cs @@ -0,0 +1,122 @@ +/* Autogenerated file. Manual edits will not be saved.*/ + +#nullable enable +using System; +using mud.Client; +using mud.Network.schemas; +using mud.Unity; +using UniRx; +using Property = System.Collections.Generic.Dictionary; + +namespace DefaultNamespace +{ + public class PositionTableUpdate : TypedRecordUpdate> { } + + public class PositionTable : IMudTable + { + public static readonly TableId TableId = new("", "Position"); + + public long? x; + public long? y; + + public static IObservable OnRecordUpdate() + { + return NetworkManager.Instance.ds.OnDataStoreUpdate + .Where( + update => + update.TableId == TableId.ToString() && update.Type == UpdateType.SetField + ) + .Select( + update => + new PositionTableUpdate + { + TableId = update.TableId, + Key = update.Key, + Value = update.Value, + TypedValue = MapUpdates(update.Value) + } + ); + } + + public static IObservable OnRecordInsert() + { + return NetworkManager.Instance.ds.OnDataStoreUpdate + .Where( + update => + update.TableId == TableId.ToString() && update.Type == UpdateType.SetRecord + ) + .Select( + update => + new PositionTableUpdate + { + TableId = update.TableId, + Key = update.Key, + Value = update.Value, + TypedValue = MapUpdates(update.Value) + } + ); + } + + public static IObservable OnRecordDelete() + { + return NetworkManager.Instance.ds.OnDataStoreUpdate + .Where( + update => + update.TableId == TableId.ToString() + && update.Type == UpdateType.DeleteRecord + ) + .Select( + update => + new PositionTableUpdate + { + TableId = update.TableId, + Key = update.Key, + Value = update.Value, + TypedValue = MapUpdates(update.Value) + } + ); + } + + public static Tuple MapUpdates( + Tuple value + ) + { + PositionTable? current = null; + PositionTable? previous = null; + + if (value.Item1 != null) + { + try + { + current = new PositionTable + { + x = value.Item1.TryGetValue("x", out var xVal) ? (long)xVal : default, + y = value.Item1.TryGetValue("y", out var yVal) ? (long)yVal : default, + }; + } + catch (InvalidCastException) + { + current = new PositionTable { x = null, y = null, }; + } + } + + if (value.Item2 != null) + { + try + { + previous = new PositionTable + { + x = value.Item2.TryGetValue("x", out var xVal) ? (long)xVal : default, + y = value.Item2.TryGetValue("y", out var yVal) ? (long)yVal : default, + }; + } + catch (InvalidCastException) + { + previous = new PositionTable { x = null, y = null, }; + } + } + + return new Tuple(current, previous); + } + } +} diff --git a/packages/client/Assets/Scripts/codegen/PositionTable.cs.meta b/packages/client/Assets/Scripts/codegen/PositionTable.cs.meta new file mode 100644 index 0000000..dc16ba6 --- /dev/null +++ b/packages/client/Assets/Scripts/codegen/PositionTable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4d6763fa2d7254fd981959338b59ba34 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/client/Packages/packages-lock.json b/packages/client/Packages/packages-lock.json index dbd562a..e44199a 100644 --- a/packages/client/Packages/packages-lock.json +++ b/packages/client/Packages/packages-lock.json @@ -12,7 +12,7 @@ "depth": 0, "source": "git", "dependencies": {}, - "hash": "0b4859426bc8c550f57e63339979244dfc65c9ba" + "hash": "e5bc8bf178b1f1b294b5e06ee7a873c8c6fe2b37" }, "com.unity.ai.navigation": { "version": "1.1.3", diff --git a/packages/contracts/mud.config.ts b/packages/contracts/mud.config.ts index fe5ab5d..421807c 100644 --- a/packages/contracts/mud.config.ts +++ b/packages/contracts/mud.config.ts @@ -2,15 +2,25 @@ import { mudConfig, resolveTableId } from "@latticexyz/world/register"; export default mudConfig({ tables: { - /* - * TODO: - * - Position: (x: int32, y: int32), - * - Health: uint32, - * - Player: bool, - * - Damage: uint32 - */ + Position: { + schema: { + x: "int32", + y: "int32", + }, + }, + Health: { + schema: { + value: "uint32", + }, + }, + Player: "bool", + Damage: "uint32", }, modules: [ - // TODO: Add reverse lookup for Position + { + name: "KeysWithValueModule", + root: true, + args: [resolveTableId("Position")], + }, ], }); diff --git a/packages/contracts/src/codegen/Tables.sol b/packages/contracts/src/codegen/Tables.sol index 4fa54c4..f95880c 100644 --- a/packages/contracts/src/codegen/Tables.sol +++ b/packages/contracts/src/codegen/Tables.sol @@ -3,4 +3,7 @@ pragma solidity >=0.8.0; /* Autogenerated file. Do not edit manually. */ -import { Counter, CounterTableId } from "./tables/Counter.sol"; +import { Position, PositionData, PositionTableId } from "./tables/Position.sol"; +import { Health, HealthTableId } from "./tables/Health.sol"; +import { Player, PlayerTableId } from "./tables/Player.sol"; +import { Damage, DamageTableId } from "./tables/Damage.sol"; diff --git a/packages/contracts/src/codegen/tables/Counter.sol b/packages/contracts/src/codegen/tables/Damage.sol similarity index 97% rename from packages/contracts/src/codegen/tables/Counter.sol rename to packages/contracts/src/codegen/tables/Damage.sol index ed69f2f..74764dd 100644 --- a/packages/contracts/src/codegen/tables/Counter.sol +++ b/packages/contracts/src/codegen/tables/Damage.sol @@ -17,10 +17,10 @@ import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCounter.sol"; -bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Counter"))); -bytes32 constant CounterTableId = _tableId; +bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Damage"))); +bytes32 constant DamageTableId = _tableId; -library Counter { +library Damage { /** Get the table's schema */ function getSchema() internal pure returns (Schema) { SchemaType[] memory _schema = new SchemaType[](1); @@ -40,7 +40,7 @@ library Counter { function getMetadata() internal pure returns (string memory, string[] memory) { string[] memory _fieldNames = new string[](1); _fieldNames[0] = "value"; - return ("Counter", _fieldNames); + return ("Damage", _fieldNames); } /** Register the table's schema */ diff --git a/packages/contracts/src/codegen/tables/Health.sol b/packages/contracts/src/codegen/tables/Health.sol new file mode 100644 index 0000000..f0b76b1 --- /dev/null +++ b/packages/contracts/src/codegen/tables/Health.sol @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/* Autogenerated file. Do not edit manually. */ + +// Import schema type +import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; + +// Import store internals +import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; +import { Bytes } from "@latticexyz/store/src/Bytes.sol"; +import { Memory } from "@latticexyz/store/src/Memory.sol"; +import { SliceLib } from "@latticexyz/store/src/Slice.sol"; +import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; +import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; +import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCounter.sol"; + +bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Health"))); +bytes32 constant HealthTableId = _tableId; + +library Health { + /** Get the table's schema */ + function getSchema() internal pure returns (Schema) { + SchemaType[] memory _schema = new SchemaType[](1); + _schema[0] = SchemaType.UINT32; + + return SchemaLib.encode(_schema); + } + + function getKeySchema() internal pure returns (Schema) { + SchemaType[] memory _schema = new SchemaType[](1); + _schema[0] = SchemaType.BYTES32; + + return SchemaLib.encode(_schema); + } + + /** Get the table's metadata */ + function getMetadata() internal pure returns (string memory, string[] memory) { + string[] memory _fieldNames = new string[](1); + _fieldNames[0] = "value"; + return ("Health", _fieldNames); + } + + /** Register the table's schema */ + function registerSchema() internal { + StoreSwitch.registerSchema(_tableId, getSchema(), getKeySchema()); + } + + /** Register the table's schema (using the specified store) */ + function registerSchema(IStore _store) internal { + _store.registerSchema(_tableId, getSchema(), getKeySchema()); + } + + /** Set the table's metadata */ + function setMetadata() internal { + (string memory _tableName, string[] memory _fieldNames) = getMetadata(); + StoreSwitch.setMetadata(_tableId, _tableName, _fieldNames); + } + + /** Set the table's metadata (using the specified store) */ + function setMetadata(IStore _store) internal { + (string memory _tableName, string[] memory _fieldNames) = getMetadata(); + _store.setMetadata(_tableId, _tableName, _fieldNames); + } + + /** Get value */ + function get(bytes32 key) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0); + return (uint32(Bytes.slice4(_blob, 0))); + } + + /** Get value (using the specified store) */ + function get(IStore _store, bytes32 key) internal view returns (uint32 value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + bytes memory _blob = _store.getField(_tableId, _keyTuple, 0); + return (uint32(Bytes.slice4(_blob, 0))); + } + + /** Set value */ + function set(bytes32 key, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value))); + } + + /** Set value (using the specified store) */ + function set(IStore _store, bytes32 key, uint32 value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value))); + } + + /** Tightly pack full data using this table's schema */ + function encode(uint32 value) internal view returns (bytes memory) { + return abi.encodePacked(value); + } + + /** Encode keys as a bytes32 array using this table's schema */ + function encodeKeyTuple(bytes32 key) internal pure returns (bytes32[] memory _keyTuple) { + _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + } + + /* Delete all data for given keys */ + function deleteRecord(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /* Delete all data for given keys (using the specified store) */ + function deleteRecord(IStore _store, bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + _store.deleteRecord(_tableId, _keyTuple); + } +} diff --git a/packages/contracts/src/codegen/tables/Player.sol b/packages/contracts/src/codegen/tables/Player.sol new file mode 100644 index 0000000..7b0b3fa --- /dev/null +++ b/packages/contracts/src/codegen/tables/Player.sol @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/* Autogenerated file. Do not edit manually. */ + +// Import schema type +import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; + +// Import store internals +import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; +import { Bytes } from "@latticexyz/store/src/Bytes.sol"; +import { Memory } from "@latticexyz/store/src/Memory.sol"; +import { SliceLib } from "@latticexyz/store/src/Slice.sol"; +import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; +import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; +import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCounter.sol"; + +bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Player"))); +bytes32 constant PlayerTableId = _tableId; + +library Player { + /** Get the table's schema */ + function getSchema() internal pure returns (Schema) { + SchemaType[] memory _schema = new SchemaType[](1); + _schema[0] = SchemaType.BOOL; + + return SchemaLib.encode(_schema); + } + + function getKeySchema() internal pure returns (Schema) { + SchemaType[] memory _schema = new SchemaType[](1); + _schema[0] = SchemaType.BYTES32; + + return SchemaLib.encode(_schema); + } + + /** Get the table's metadata */ + function getMetadata() internal pure returns (string memory, string[] memory) { + string[] memory _fieldNames = new string[](1); + _fieldNames[0] = "value"; + return ("Player", _fieldNames); + } + + /** Register the table's schema */ + function registerSchema() internal { + StoreSwitch.registerSchema(_tableId, getSchema(), getKeySchema()); + } + + /** Register the table's schema (using the specified store) */ + function registerSchema(IStore _store) internal { + _store.registerSchema(_tableId, getSchema(), getKeySchema()); + } + + /** Set the table's metadata */ + function setMetadata() internal { + (string memory _tableName, string[] memory _fieldNames) = getMetadata(); + StoreSwitch.setMetadata(_tableId, _tableName, _fieldNames); + } + + /** Set the table's metadata (using the specified store) */ + function setMetadata(IStore _store) internal { + (string memory _tableName, string[] memory _fieldNames) = getMetadata(); + _store.setMetadata(_tableId, _tableName, _fieldNames); + } + + /** Get value */ + function get(bytes32 key) internal view returns (bool value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0); + return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + } + + /** Get value (using the specified store) */ + function get(IStore _store, bytes32 key) internal view returns (bool value) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + bytes memory _blob = _store.getField(_tableId, _keyTuple, 0); + return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + } + + /** Set value */ + function set(bytes32 key, bool value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value))); + } + + /** Set value (using the specified store) */ + function set(IStore _store, bytes32 key, bool value) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value))); + } + + /** Tightly pack full data using this table's schema */ + function encode(bool value) internal view returns (bytes memory) { + return abi.encodePacked(value); + } + + /** Encode keys as a bytes32 array using this table's schema */ + function encodeKeyTuple(bytes32 key) internal pure returns (bytes32[] memory _keyTuple) { + _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + } + + /* Delete all data for given keys */ + function deleteRecord(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /* Delete all data for given keys (using the specified store) */ + function deleteRecord(IStore _store, bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + _store.deleteRecord(_tableId, _keyTuple); + } +} + +function _toBool(uint8 value) pure returns (bool result) { + assembly { + result := value + } +} diff --git a/packages/contracts/src/codegen/tables/Position.sol b/packages/contracts/src/codegen/tables/Position.sol new file mode 100644 index 0000000..84d1d31 --- /dev/null +++ b/packages/contracts/src/codegen/tables/Position.sol @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/* Autogenerated file. Do not edit manually. */ + +// Import schema type +import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; + +// Import store internals +import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; +import { Bytes } from "@latticexyz/store/src/Bytes.sol"; +import { Memory } from "@latticexyz/store/src/Memory.sol"; +import { SliceLib } from "@latticexyz/store/src/Slice.sol"; +import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; +import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; +import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCounter.sol"; + +bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Position"))); +bytes32 constant PositionTableId = _tableId; + +struct PositionData { + int32 x; + int32 y; +} + +library Position { + /** Get the table's schema */ + function getSchema() internal pure returns (Schema) { + SchemaType[] memory _schema = new SchemaType[](2); + _schema[0] = SchemaType.INT32; + _schema[1] = SchemaType.INT32; + + return SchemaLib.encode(_schema); + } + + function getKeySchema() internal pure returns (Schema) { + SchemaType[] memory _schema = new SchemaType[](1); + _schema[0] = SchemaType.BYTES32; + + return SchemaLib.encode(_schema); + } + + /** Get the table's metadata */ + function getMetadata() internal pure returns (string memory, string[] memory) { + string[] memory _fieldNames = new string[](2); + _fieldNames[0] = "x"; + _fieldNames[1] = "y"; + return ("Position", _fieldNames); + } + + /** Register the table's schema */ + function registerSchema() internal { + StoreSwitch.registerSchema(_tableId, getSchema(), getKeySchema()); + } + + /** Register the table's schema (using the specified store) */ + function registerSchema(IStore _store) internal { + _store.registerSchema(_tableId, getSchema(), getKeySchema()); + } + + /** Set the table's metadata */ + function setMetadata() internal { + (string memory _tableName, string[] memory _fieldNames) = getMetadata(); + StoreSwitch.setMetadata(_tableId, _tableName, _fieldNames); + } + + /** Set the table's metadata (using the specified store) */ + function setMetadata(IStore _store) internal { + (string memory _tableName, string[] memory _fieldNames) = getMetadata(); + _store.setMetadata(_tableId, _tableName, _fieldNames); + } + + /** Get x */ + function getX(bytes32 key) internal view returns (int32 x) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0); + return (int32(uint32(Bytes.slice4(_blob, 0)))); + } + + /** Get x (using the specified store) */ + function getX(IStore _store, bytes32 key) internal view returns (int32 x) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + bytes memory _blob = _store.getField(_tableId, _keyTuple, 0); + return (int32(uint32(Bytes.slice4(_blob, 0)))); + } + + /** Set x */ + function setX(bytes32 key, int32 x) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((x))); + } + + /** Set x (using the specified store) */ + function setX(IStore _store, bytes32 key, int32 x) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((x))); + } + + /** Get y */ + function getY(bytes32 key) internal view returns (int32 y) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 1); + return (int32(uint32(Bytes.slice4(_blob, 0)))); + } + + /** Get y (using the specified store) */ + function getY(IStore _store, bytes32 key) internal view returns (int32 y) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + bytes memory _blob = _store.getField(_tableId, _keyTuple, 1); + return (int32(uint32(Bytes.slice4(_blob, 0)))); + } + + /** Set y */ + function setY(bytes32 key, int32 y) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + StoreSwitch.setField(_tableId, _keyTuple, 1, abi.encodePacked((y))); + } + + /** Set y (using the specified store) */ + function setY(IStore _store, bytes32 key, int32 y) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + _store.setField(_tableId, _keyTuple, 1, abi.encodePacked((y))); + } + + /** Get the full data */ + function get(bytes32 key) internal view returns (PositionData memory _table) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + bytes memory _blob = StoreSwitch.getRecord(_tableId, _keyTuple, getSchema()); + return decode(_blob); + } + + /** Get the full data (using the specified store) */ + function get(IStore _store, bytes32 key) internal view returns (PositionData memory _table) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + bytes memory _blob = _store.getRecord(_tableId, _keyTuple, getSchema()); + return decode(_blob); + } + + /** Set the full data using individual values */ + function set(bytes32 key, int32 x, int32 y) internal { + bytes memory _data = encode(x, y); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + StoreSwitch.setRecord(_tableId, _keyTuple, _data); + } + + /** Set the full data using individual values (using the specified store) */ + function set(IStore _store, bytes32 key, int32 x, int32 y) internal { + bytes memory _data = encode(x, y); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + _store.setRecord(_tableId, _keyTuple, _data); + } + + /** Set the full data using the data struct */ + function set(bytes32 key, PositionData memory _table) internal { + set(key, _table.x, _table.y); + } + + /** Set the full data using the data struct (using the specified store) */ + function set(IStore _store, bytes32 key, PositionData memory _table) internal { + set(_store, key, _table.x, _table.y); + } + + /** Decode the tightly packed blob using this table's schema */ + function decode(bytes memory _blob) internal pure returns (PositionData memory _table) { + _table.x = (int32(uint32(Bytes.slice4(_blob, 0)))); + + _table.y = (int32(uint32(Bytes.slice4(_blob, 4)))); + } + + /** Tightly pack full data using this table's schema */ + function encode(int32 x, int32 y) internal view returns (bytes memory) { + return abi.encodePacked(x, y); + } + + /** Encode keys as a bytes32 array using this table's schema */ + function encodeKeyTuple(bytes32 key) internal pure returns (bytes32[] memory _keyTuple) { + _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + } + + /* Delete all data for given keys */ + function deleteRecord(bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /* Delete all data for given keys (using the specified store) */ + function deleteRecord(IStore _store, bytes32 key) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32((key)); + + _store.deleteRecord(_tableId, _keyTuple); + } +} diff --git a/packages/contracts/src/codegen/world/IWorld.sol b/packages/contracts/src/codegen/world/IWorld.sol index 33627b4..62f36f1 100644 --- a/packages/contracts/src/codegen/world/IWorld.sol +++ b/packages/contracts/src/codegen/world/IWorld.sol @@ -6,13 +6,12 @@ pragma solidity >=0.8.0; import { IBaseWorld } from "@latticexyz/world/src/interfaces/IBaseWorld.sol"; import { IAttackSystem } from "./IAttackSystem.sol"; -import { IIncrementSystem } from "./IIncrementSystem.sol"; import { IMoveSystem } from "./IMoveSystem.sol"; /** * The IWorld interface includes all systems dynamically added to the World * during the deploy process. */ -interface IWorld is IBaseWorld, IAttackSystem, IIncrementSystem, IMoveSystem { +interface IWorld is IBaseWorld, IAttackSystem, IMoveSystem { } diff --git a/packages/contracts/src/systems/AttackSystem.sol b/packages/contracts/src/systems/AttackSystem.sol index 495a2c4..bc5b143 100644 --- a/packages/contracts/src/systems/AttackSystem.sol +++ b/packages/contracts/src/systems/AttackSystem.sol @@ -2,57 +2,53 @@ pragma solidity >=0.8.0; import { System } from "@latticexyz/world/src/System.sol"; import { getKeysWithValue } from "@latticexyz/world/src/modules/keyswithvalue/getKeysWithValue.sol"; -// TODO: import tables +import { Damage, Position, PositionTableId, Player, PositionData, Health } from "../codegen/Tables.sol"; import { addressToEntityKey } from "../addressToEntityKey.sol"; contract AttackSystem is System { function attack(int32 x, int32 y) public { bytes32 player = addressToEntityKey(address(_msgSender())); - // TODO: get all coords surrounding the target (including the target) - // PositionData[] memory neighbors = mooreNeighborhood(PositionData(x, y)); - - // TODO: iterate over all coords surrounding the target - // for (uint i = 0; i < neighbors.length; i++) { - // PositionData memory neighbor = neighbors[i]; - // bytes32[] memory atPosition = getKeysWithValue(PositionTableId, Position.encode(neighbor.x, neighbor.y)); - // if (atPosition.length == 1) { - // attackTarget(player, atPosition); - // } - // } + PositionData[] memory neighbors = mooreNeighborhood(PositionData(x, y)); + + for (uint i = 0; i < neighbors.length; i++) { + PositionData memory neighbor = neighbors[i]; + bytes32[] memory atPosition = getKeysWithValue(PositionTableId, Position.encode(neighbor.x, neighbor.y)); + if (atPosition.length == 1) { + attackTarget(player, atPosition); + } + } + } + + function attackTarget(bytes32 player, bytes32[] memory atPosition) internal { + bytes32 defender = atPosition[0]; + + require(Player.get(defender), "target is not a player"); + require(Health.get(defender) > 0, "target is dead"); + + uint32 playerDamage = Damage.get(player); + uint32 defenderHealth = Health.get(defender); + uint32 newHealth = defenderHealth - playerDamage; + if (newHealth <= 0) { + Health.deleteRecord(defender); + Position.deleteRecord(defender); + Player.deleteRecord(defender); + } else { + Health.set(defender, newHealth); + } } - // TODO - // function attackTarget(bytes32 player, bytes32[] memory atPosition) internal { - // bytes32 defender = atPosition[0]; - - // require(Player.get(defender), "target is not a player"); - // require(Health.get(defender) > 0, "target is dead"); - - // uint32 playerDamage = Damage.get(player); - // uint32 defenderHealth = Health.get(defender); - // uint32 newHealth = defenderHealth - playerDamage; - // if (newHealth <= 0) { - // Health.deleteRecord(defender); - // Position.deleteRecord(defender); - // Player.deleteRecord(defender); - // } else { - // Health.set(defender, newHealth); - // } - // } - -// TODO: Uncomment once you have the Position Table set up -// function mooreNeighborhood(PositionData memory center) internal pure returns (PositionData[] memory) { -// PositionData[] memory neighbors = new PositionData[](9); -// uint256 index = 0; - -// for (int32 x = -1; x <= 1; x++) { -// for (int32 y = -1; y <= 1; y++) { -// neighbors[index] = PositionData(center.x + x, center.y + y); -// index++; -// } -// } - -// return neighbors; -// } +function mooreNeighborhood(PositionData memory center) internal pure returns (PositionData[] memory) { + PositionData[] memory neighbors = new PositionData[](9); + uint256 index = 0; + + for (int32 x = -1; x <= 1; x++) { + for (int32 y = -1; y <= 1; y++) { + neighbors[index] = PositionData(center.x + x, center.y + y); + index++; + } + } + + return neighbors; +} } diff --git a/packages/contracts/src/systems/MoveSystem.sol b/packages/contracts/src/systems/MoveSystem.sol index 6b7e7ed..e0cfdda 100644 --- a/packages/contracts/src/systems/MoveSystem.sol +++ b/packages/contracts/src/systems/MoveSystem.sol @@ -2,7 +2,8 @@ pragma solidity >=0.8.0; import { System } from "@latticexyz/world/src/System.sol"; import { IStore } from "@latticexyz/store/src/IStore.sol"; -// TODO: import tables +import { Position, PositionTableId, Health, Player, Damage } from "../codegen/Tables.sol"; +import { getKeysWithValue } from "@latticexyz/world/src/modules/keyswithvalue/getKeysWithValue.sol"; import { addressToEntityKey } from "../addressToEntityKey.sol"; contract MoveSystem is System { @@ -10,19 +11,19 @@ contract MoveSystem is System { // Get player key bytes32 player = addressToEntityKey(address(_msgSender())); - // TODO: check if there is a player at the position + bytes32[] memory atPosition = getKeysWithValue(PositionTableId, Position.encode(x, y)); + require(atPosition.length == 0, "position occupied"); - // TODO: Set position + Position.set(player, x, y); } function spawn(int32 x, int32 y) public { bytes32 player = addressToEntityKey(address(_msgSender())); - // TODO: Check if player has already spawned + require(!Player.get(player), "already spawned"); - // TODO: set components for our player - // Player.set(player, true); - // Position.set(player, x, y); - // Health.set(player, 100); - // Damage.set(player, 10); + Player.set(player, true); + Position.set(player, x, y); + Health.set(player, 100); + Damage.set(player, 10); } } \ No newline at end of file diff --git a/packages/contracts/unity/templates/DefinitionTemplate.ejs b/packages/contracts/unity/templates/DefinitionTemplate.ejs index 304ee94..74cec4c 100644 --- a/packages/contracts/unity/templates/DefinitionTemplate.ejs +++ b/packages/contracts/unity/templates/DefinitionTemplate.ejs @@ -20,38 +20,6 @@ namespace <%= namespace %> public <%= field.type %>? <%= field.key %>; <% } -%> - public static <%= tableClassName %>? GetTableValue(string key) - { - var query = new Query().Find("?value", "?attribute").Where(TableId.ToString(), key, "?attribute", "?value"); - var result = NetworkManager.Instance.ds.Query(query); - var <%= tableClassName[0].toLowerCase() + tableClassName.substring(1) %> = new <%= tableClassName %>(); - var hasValues = false; - - foreach (var record in result) - { - var attribute = record["attribute"].ToString(); - var value = record["value"]; - - switch (attribute) - { - <% for (const field of fields) { -%> - case "<%= field.key %>": - var <%= field.key %>Value = - <% if (field.type == "System.Numerics.BigInteger") { -%> - new System.Numerics.BigInteger((int)value); - <% } else { -%> - (<%= field.type %>)value; - <% } -%> - <%= tableClassName[0].toLowerCase() + tableClassName.substring(1) %>.<%= field.key %> = <%= field.key %>Value; - hasValues = true; - break; - <% } -%> - } - } - - return hasValues ? <%= tableClassName[0].toLowerCase() + tableClassName.substring(1) %> : null; - } - public static IObservable<<%= tableClassName%>Update> OnRecordUpdate() { return NetworkManager.Instance.ds.OnDataStoreUpdate.Where(update => update.TableId == TableId.ToString() && update.Type == UpdateType.SetField)