From 5b3a7cab26675d1a1e1cc1afffe83df3fad07779 Mon Sep 17 00:00:00 2001 From: lermchair <13699109+lermchair@users.noreply.github.com> Date: Fri, 19 May 2023 13:16:55 -0400 Subject: [PATCH 1/3] final version --- packages/client/Assets/Resources/Tank.prefab | 16 +- packages/client/Assets/Resources/latest.json | 2 +- packages/client/Assets/Scenes/Main.unity | 95 ++++++++ .../ContractDefinition/IWorldDefinition.cs | 10 - .../Scripts/IWorld/Service/IWorldService.cs | 20 -- .../client/Assets/Scripts/PlayerController.cs | 36 ++- .../client/Assets/Scripts/PlayerManager.cs | 29 ++- packages/client/Assets/Scripts/TankHealth.cs | 122 ++++++---- .../client/Assets/Scripts/TankShooting.cs | 4 +- .../{CounterTable.cs => DamageTable.cs} | 42 ++-- ...unterTable.cs.meta => DamageTable.cs.meta} | 2 +- .../Assets/Scripts/codegen/HealthTable.cs | 150 ++++++++++++ .../Scripts/codegen/HealthTable.cs.meta | 11 + .../Assets/Scripts/codegen/PlayerTable.cs | 150 ++++++++++++ .../Scripts/codegen/PlayerTable.cs.meta | 11 + .../Assets/Scripts/codegen/PositionTable.cs | 154 ++++++++++++ .../Scripts/codegen/PositionTable.cs.meta | 11 + packages/contracts/mud.config.ts | 26 +- packages/contracts/src/codegen/Tables.sol | 5 +- .../tables/{Counter.sol => Damage.sol} | 8 +- .../contracts/src/codegen/tables/Health.sol | 128 ++++++++++ .../contracts/src/codegen/tables/Player.sol | 134 +++++++++++ .../contracts/src/codegen/tables/Position.sol | 224 ++++++++++++++++++ .../contracts/src/codegen/world/IWorld.sol | 3 +- .../contracts/src/systems/AttackSystem.sol | 86 ++++--- packages/contracts/src/systems/MoveSystem.sol | 19 +- 26 files changed, 1296 insertions(+), 202 deletions(-) rename packages/client/Assets/Scripts/codegen/{CounterTable.cs => DamageTable.cs} (75%) rename packages/client/Assets/Scripts/codegen/{CounterTable.cs.meta => DamageTable.cs.meta} (83%) create mode 100644 packages/client/Assets/Scripts/codegen/HealthTable.cs create mode 100644 packages/client/Assets/Scripts/codegen/HealthTable.cs.meta create mode 100644 packages/client/Assets/Scripts/codegen/PlayerTable.cs create mode 100644 packages/client/Assets/Scripts/codegen/PlayerTable.cs.meta create mode 100644 packages/client/Assets/Scripts/codegen/PositionTable.cs create mode 100644 packages/client/Assets/Scripts/codegen/PositionTable.cs.meta rename packages/contracts/src/codegen/tables/{Counter.sol => Damage.sol} (97%) create mode 100644 packages/contracts/src/codegen/tables/Health.sol create mode 100644 packages/contracts/src/codegen/tables/Player.sol create mode 100644 packages/contracts/src/codegen/tables/Position.sol diff --git a/packages/client/Assets/Resources/Tank.prefab b/packages/client/Assets/Resources/Tank.prefab index 0950e83..114d673 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 @@ -685,7 +686,7 @@ Transform: - {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 +768,19 @@ 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} +--- !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 diff --git a/packages/client/Assets/Resources/latest.json b/packages/client/Assets/Resources/latest.json index d027302..30b245f 100644 --- a/packages/client/Assets/Resources/latest.json +++ b/packages/client/Assets/Resources/latest.json @@ -1,4 +1,4 @@ { "worldAddress": "0x5FbDB2315678afecb367f032d93F642f64180aa3", - "blockNumber": 435 + "blockNumber": 22 } \ No newline at end of file diff --git a/packages/client/Assets/Scenes/Main.unity b/packages/client/Assets/Scenes/Main.unity index b720c6f..32d978c 100644 --- a/packages/client/Assets/Scenes/Main.unity +++ b/packages/client/Assets/Scenes/Main.unity @@ -851,6 +851,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 +1154,56 @@ 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 + pk: + chainId: 31337 + contractAddress: + 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} 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..72ec289 100644 --- a/packages/client/Assets/Scripts/PlayerController.cs +++ b/packages/client/Assets/Scripts/PlayerController.cs @@ -21,8 +21,7 @@ public class PlayerController : MonoBehaviour private bool _hasDestination; private IDisposable? _disposer; private TankShooting _target; - // TODO: Get PlayerSync component - // TODO: Get NetworkManager + private PlayerSync _player; void Start() { @@ -31,24 +30,23 @@ void Start() if (target == null) return; _target = target; - // TODO: Get NetworkManager + _player = GetComponent(); - // TODO: Get player sync - - // TODO: Subscribe to Position table + var sub = PositionTable.OnRecordInsert().Merge(PositionTable.OnRecordUpdate()); + _disposer = ObservableExtensions.Subscribe(sub.ObserveOnMainThread(), OnChainPositionUpdate); } // 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); - // } + 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 @@ -56,7 +54,7 @@ private async UniTaskVoid SendMoveTxAsync(int x, int y) { try { - // TODO: Send tx from NetworkManager + await NetworkManager.Instance.worldSend.TxExecute(x, y); } catch (Exception ex) { @@ -92,7 +90,7 @@ void Update() } // TODO: Early return if not local player - if (_target.RangeVisible) return; + if (!_player.IsLocalPlayer() || _target.RangeVisible) return; if (Input.GetMouseButtonDown(0)) { var ray = _camera.ScreenPointToRay(Input.mousePosition); @@ -111,7 +109,7 @@ void Update() } _destinationMarker = Instantiate(destinationMarker, dest, Quaternion.identity); - // TODO: Send Tx + SendMoveTxAsync(Convert.ToInt32(dest.x), Convert.ToInt32(dest.z)).Forget(); } } diff --git a/packages/client/Assets/Scripts/PlayerManager.cs b/packages/client/Assets/Scripts/PlayerManager.cs index f1ceb3f..c6a0e94 100644 --- a/packages/client/Assets/Scripts/PlayerManager.cs +++ b/packages/client/Assets/Scripts/PlayerManager.cs @@ -22,16 +22,33 @@ 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 currentPlayer = PlayerTable.GetTableValue(addressKey); + if (currentPlayer == null) + { + await nm.worldSend.TxExecute(0, 0); + } - // TODO: Subscribe to PlayerTable + var playerSub = PlayerTable.OnRecordInsert().ObserveOnMainThread().Subscribe(OnUpdatePlayers); + _disposers.Add(playerSub); } // TODO: Callback for PlayerTable update - // private void OnUpdatePlayers(PlayerTableUpdate update) - // { - // } + private void OnUpdatePlayers(PlayerTableUpdate update) + { + var currentValue = update.TypedValue.Item1; + if (currentValue == null) return; + var playerPosition = PositionTable.GetTableValue(update.Key); + if (playerPosition == null) return; + var playerSpawnPoint = new Vector3((float)playerPosition.x, 0, (float)playerPosition.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 = update.Key; + if (update.Key != net.addressKey) return; + PlayerSync.localPlayerKey = update.Key; + } private void OnDestroy() { diff --git a/packages/client/Assets/Scripts/TankHealth.cs b/packages/client/Assets/Scripts/TankHealth.cs index 6441f45..5fb03ce 100644 --- a/packages/client/Assets/Scripts/TankHealth.cs +++ b/packages/client/Assets/Scripts/TankHealth.cs @@ -8,69 +8,87 @@ public class TankHealth : MonoBehaviour { - public float m_StartingHealth = 100f; - private float m_CurrentHealth; + public float m_StartingHealth = 100f; + private float m_CurrentHealth; - public Slider m_Slider; - public Image m_FillImage; - public Color m_FullHealthColor = Color.green; - public Color m_ZeroHealthColor = Color.red; - public GameObject m_ExplosionPrefab; - public GameObject shell; + public Slider m_Slider; + public Image m_FillImage; + public Color m_FullHealthColor = Color.green; + public Color m_ZeroHealthColor = Color.red; + public GameObject m_ExplosionPrefab; + public GameObject shell; + private PlayerSync _player; + private ParticleSystem m_ExplosionParticles; + private bool m_Dead; + private CompositeDisposable _disposable = new(); - // TODO: Get PlayerSync - private ParticleSystem m_ExplosionParticles; - private bool m_Dead; - private CompositeDisposable _disposable = new(); + private void Awake() + { + m_ExplosionParticles = Instantiate(m_ExplosionPrefab).GetComponent(); + m_ExplosionParticles.gameObject.SetActive(false); + _player = GetComponent(); + var healthUpdated = ObservableExtensions.Subscribe(HealthTable.OnRecordUpdate().ObserveOnMainThread(), OnHealthChange); + var healthDeleted = ObservableExtensions.Subscribe(HealthTable.OnRecordDelete().ObserveOnMainThread(), OnPlayerDeath); + _disposable.Add(healthUpdated); + _disposable.Add(healthDeleted); + } - 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 - } + private void OnEnable() + { + m_CurrentHealth = m_StartingHealth; + m_Dead = false; + SetHealthUI(); + } - private void OnEnable() - { - m_CurrentHealth = m_StartingHealth; - m_Dead = false; + // TODO: Callback for HealthTable update + private void OnHealthChange(HealthTableUpdate update) + { + if (update.Key != _player.key) return; + var initialShellPosition = transform.position; + initialShellPosition.y += 10; + Instantiate(shell, initialShellPosition, Quaternion.LookRotation(Vector3.down)); + var currentValue = update.TypedValue.Item1; - SetHealthUI(); - } + m_CurrentHealth = Convert.ToSingle(currentValue.value); + SetHealthUI(); - // TODO: Callback for HealthTable update - // private void OnHealthChange(HealthTableUpdate update) - // { - // } + } - // TODO: Callback for HealthTable deletion - // private void OnPlayerDeath(HealthTableUpdate update) - // { - // } + // TODO: Callback for HealthTable deletion + private void OnPlayerDeath(HealthTableUpdate update) + { + { + if (update.Key != _player.key) return; - private void SetHealthUI() - { - // Adjust the value and colour of the slider. - m_Slider.value = m_CurrentHealth; - m_FillImage.color = Color.Lerp(m_ZeroHealthColor, m_FullHealthColor, m_CurrentHealth / m_StartingHealth); - } + if (update.Value.Item1 == null) + { + OnDeath(); + return; + } + } + } - private void OnDeath() - { - m_Dead = true; - m_ExplosionParticles.transform.position = transform.position; - m_ExplosionParticles.gameObject.SetActive(true); - m_ExplosionParticles.Play(); - gameObject.SetActive(false); - } + private void SetHealthUI() + { + // Adjust the value and colour of the slider. + m_Slider.value = m_CurrentHealth; + m_FillImage.color = Color.Lerp(m_ZeroHealthColor, m_FullHealthColor, m_CurrentHealth / m_StartingHealth); + } - private void OnDestroy() - { - _disposable?.Dispose(); - } + private void OnDeath() + { + m_Dead = true; + m_ExplosionParticles.transform.position = transform.position; + m_ExplosionParticles.gameObject.SetActive(true); + m_ExplosionParticles.Play(); + gameObject.SetActive(false); + } + + private void OnDestroy() + { + _disposable?.Dispose(); + } } 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 75% rename from packages/client/Assets/Scripts/codegen/CounterTable.cs rename to packages/client/Assets/Scripts/codegen/DamageTable.cs index 430cee6..0214125 100644 --- a/packages/client/Assets/Scripts/codegen/CounterTable.cs +++ b/packages/client/Assets/Scripts/codegen/DamageTable.cs @@ -10,21 +10,21 @@ 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) + public static DamageTable? 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 damageTable = new DamageTable(); var hasValues = false; foreach (var record in result) @@ -36,16 +36,16 @@ public class CounterTable : IMudTable { case "value": var valueValue = (ulong)value; - counterTable.value = valueValue; + damageTable.value = valueValue; hasValues = true; break; } } - return hasValues ? counterTable : null; + return hasValues ? damageTable : null; } - public static IObservable OnRecordUpdate() + public static IObservable OnRecordUpdate() { return NetworkManager.Instance.ds.OnDataStoreUpdate .Where( @@ -54,7 +54,7 @@ public static IObservable OnRecordUpdate() ) .Select( update => - new CounterTableUpdate + new DamageTableUpdate { TableId = update.TableId, Key = update.Key, @@ -64,7 +64,7 @@ public static IObservable OnRecordUpdate() ); } - public static IObservable OnRecordInsert() + public static IObservable OnRecordInsert() { return NetworkManager.Instance.ds.OnDataStoreUpdate .Where( @@ -73,7 +73,7 @@ public static IObservable OnRecordInsert() ) .Select( update => - new CounterTableUpdate + new DamageTableUpdate { TableId = update.TableId, Key = update.Key, @@ -83,7 +83,7 @@ public static IObservable OnRecordInsert() ); } - public static IObservable OnRecordDelete() + public static IObservable OnRecordDelete() { return NetworkManager.Instance.ds.OnDataStoreUpdate .Where( @@ -93,7 +93,7 @@ public static IObservable OnRecordDelete() ) .Select( update => - new CounterTableUpdate + new DamageTableUpdate { TableId = update.TableId, Key = update.Key, @@ -103,18 +103,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 +123,7 @@ public static IObservable OnRecordDelete() } catch (InvalidCastException) { - current = new CounterTable { value = null, }; + current = new DamageTable { value = null, }; } } @@ -131,7 +131,7 @@ public static IObservable OnRecordDelete() { try { - previous = new CounterTable + previous = new DamageTable { value = value.Item2.TryGetValue("value", out var valueVal) ? (ulong)valueVal @@ -140,11 +140,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..39c4806 --- /dev/null +++ b/packages/client/Assets/Scripts/codegen/HealthTable.cs @@ -0,0 +1,150 @@ +/* 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 HealthTable? GetTableValue(string key) + { + var query = new Query() + .Find("?value", "?attribute") + .Where(TableId.ToString(), key, "?attribute", "?value"); + var result = NetworkManager.Instance.ds.Query(query); + var healthTable = new HealthTable(); + 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; + healthTable.value = valueValue; + hasValues = true; + break; + } + } + + return hasValues ? healthTable : null; + } + + 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..a8181b4 --- /dev/null +++ b/packages/client/Assets/Scripts/codegen/PlayerTable.cs @@ -0,0 +1,150 @@ +/* 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 PlayerTable? GetTableValue(string key) + { + var query = new Query() + .Find("?value", "?attribute") + .Where(TableId.ToString(), key, "?attribute", "?value"); + var result = NetworkManager.Instance.ds.Query(query); + var playerTable = new PlayerTable(); + var hasValues = false; + + foreach (var record in result) + { + var attribute = record["attribute"].ToString(); + var value = record["value"]; + + switch (attribute) + { + case "value": + var valueValue = (bool)value; + playerTable.value = valueValue; + hasValues = true; + break; + } + } + + return hasValues ? playerTable : null; + } + + 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..a4c3100 --- /dev/null +++ b/packages/client/Assets/Scripts/codegen/PositionTable.cs @@ -0,0 +1,154 @@ +/* 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 PositionTable? GetTableValue(string key) + { + var query = new Query() + .Find("?value", "?attribute") + .Where(TableId.ToString(), key, "?attribute", "?value"); + var result = NetworkManager.Instance.ds.Query(query); + var positionTable = new PositionTable(); + var hasValues = false; + + foreach (var record in result) + { + var attribute = record["attribute"].ToString(); + var value = record["value"]; + + switch (attribute) + { + case "x": + var xValue = (long)value; + positionTable.x = xValue; + hasValues = true; + break; + case "y": + var yValue = (long)value; + positionTable.y = yValue; + hasValues = true; + break; + } + } + + return hasValues ? positionTable : null; + } + + 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/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 From 54f034cc7968397b9c526dabf9a08122a0899a32 Mon Sep 17 00:00:00 2001 From: lermchair <13699109+lermchair@users.noreply.github.com> Date: Mon, 24 Jul 2023 12:06:51 -0700 Subject: [PATCH 2/3] update to latest unimud --- packages/client/Assets/Resources/latest.json | 2 +- packages/client/Assets/Scenes/Main.unity | 3 +- .../client/Assets/Scripts/PlayerController.cs | 219 +++++++++--------- .../client/Assets/Scripts/PlayerManager.cs | 39 ++-- packages/client/Assets/Scripts/TankHealth.cs | 143 ++++++------ .../Assets/Scripts/codegen/DamageTable.cs | 27 --- .../Assets/Scripts/codegen/HealthTable.cs | 27 --- .../Assets/Scripts/codegen/PlayerTable.cs | 27 --- .../Assets/Scripts/codegen/PositionTable.cs | 32 --- packages/client/Packages/manifest.json | 2 +- packages/client/Packages/packages-lock.json | 7 +- .../unity/templates/DefinitionTemplate.ejs | 32 --- 12 files changed, 218 insertions(+), 342 deletions(-) diff --git a/packages/client/Assets/Resources/latest.json b/packages/client/Assets/Resources/latest.json index 30b245f..4b7723f 100644 --- a/packages/client/Assets/Resources/latest.json +++ b/packages/client/Assets/Resources/latest.json @@ -1,4 +1,4 @@ { "worldAddress": "0x5FbDB2315678afecb367f032d93F642f64180aa3", - "blockNumber": 22 + "blockNumber": 12 } \ No newline at end of file diff --git a/packages/client/Assets/Scenes/Main.unity b/packages/client/Assets/Scenes/Main.unity index 32d978c..8ee4be4 100644 --- a/packages/client/Assets/Scenes/Main.unity +++ b/packages/client/Assets/Scenes/Main.unity @@ -1185,9 +1185,10 @@ MonoBehaviour: m_EditorClassIdentifier: jsonRpcUrl: http://localhost:8545 wsRpcUrl: ws://localhost:8545 - pk: chainId: 31337 contractAddress: + pk: + uniqueWallets: 0 disableCache: 1 --- !u!4 &1290505664 Transform: diff --git a/packages/client/Assets/Scripts/PlayerController.cs b/packages/client/Assets/Scripts/PlayerController.cs index 72ec289..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,109 +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; - private PlayerSync _player; - - void Start() - { - _camera = Camera.main; - var target = FindObjectOfType(); - if (target == null) return; - _target = target; - - _player = GetComponent(); - - var sub = PositionTable.OnRecordInsert().Merge(PositionTable.OnRecordUpdate()); - _disposer = ObservableExtensions.Subscribe(sub.ObserveOnMainThread(), OnChainPositionUpdate); - } - - // 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 - { - 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(); - } + 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 c6a0e94..f512db2 100644 --- a/packages/client/Assets/Scripts/PlayerManager.cs +++ b/packages/client/Assets/Scripts/PlayerManager.cs @@ -1,6 +1,9 @@ +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; @@ -23,31 +26,37 @@ void Start() async void Spawn(NetworkManager nm) { var addressKey = net.addressKey; - var currentPlayer = PlayerTable.GetTableValue(addressKey); + var playerTable = new TableId("", "Player"); + var currentPlayer = net.ds.GetValue(playerTable, addressKey); if (currentPlayer == null) { await nm.worldSend.TxExecute(0, 0); } - var playerSub = PlayerTable.OnRecordInsert().ObserveOnMainThread().Subscribe(OnUpdatePlayers); + 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) { - var currentValue = update.TypedValue.Item1; - if (currentValue == null) return; - var playerPosition = PositionTable.GetTableValue(update.Key); - if (playerPosition == null) return; - var playerSpawnPoint = new Vector3((float)playerPosition.x, 0, (float)playerPosition.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 = update.Key; - if (update.Key != net.addressKey) return; - PlayerSync.localPlayerKey = update.Key; + 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 5fb03ce..db8cd23 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; @@ -8,87 +12,86 @@ public class TankHealth : MonoBehaviour { - public float m_StartingHealth = 100f; - private float m_CurrentHealth; + public float m_StartingHealth = 100f; + private float m_CurrentHealth; - public Slider m_Slider; - public Image m_FillImage; - public Color m_FullHealthColor = Color.green; - public Color m_ZeroHealthColor = Color.red; - public GameObject m_ExplosionPrefab; - public GameObject shell; - private PlayerSync _player; - private ParticleSystem m_ExplosionParticles; - private bool m_Dead; - private CompositeDisposable _disposable = new(); + public Slider m_Slider; + public Image m_FillImage; + public Color m_FullHealthColor = Color.green; + public Color m_ZeroHealthColor = Color.red; + public GameObject m_ExplosionPrefab; + public GameObject shell; + private 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); - _player = GetComponent(); - var healthUpdated = ObservableExtensions.Subscribe(HealthTable.OnRecordUpdate().ObserveOnMainThread(), OnHealthChange); - var healthDeleted = ObservableExtensions.Subscribe(HealthTable.OnRecordDelete().ObserveOnMainThread(), OnPlayerDeath); - _disposable.Add(healthUpdated); - _disposable.Add(healthDeleted); - } + private void Awake() + { + m_ExplosionParticles = Instantiate(m_ExplosionPrefab).GetComponent(); + m_ExplosionParticles.gameObject.SetActive(false); + _player = GetComponent(); + net = NetworkManager.Instance; + var healthTable = new TableId("", "Health"); + var healthUpdated = new Query().In(healthTable); + var sub = ObservableExtensions.Subscribe(net.ds.RxQuery(healthUpdated).ObserveOnMainThread(), OnHealthChange); + _disposable.Add(sub); + } - private void OnEnable() - { - m_CurrentHealth = m_StartingHealth; - m_Dead = false; - SetHealthUI(); - } + private void OnEnable() + { + m_CurrentHealth = m_StartingHealth; + m_Dead = false; - // TODO: Callback for HealthTable update - private void OnHealthChange(HealthTableUpdate update) - { - if (update.Key != _player.key) return; - var initialShellPosition = transform.position; - initialShellPosition.y += 10; - Instantiate(shell, initialShellPosition, Quaternion.LookRotation(Vector3.down)); - var currentValue = update.TypedValue.Item1; + SetHealthUI(); + } - m_CurrentHealth = Convert.ToSingle(currentValue.value); - SetHealthUI(); + // TODO: Callback for HealthTable update + private void OnHealthChange((List SetRecords, List RemovedRecords) update) + { + Debug.Log("SET: " + JsonConvert.SerializeObject(update.SetRecords)); + Debug.Log("REMOVED: " + JsonConvert.SerializeObject(update.RemovedRecords)); + foreach (var setRecord in update.SetRecords) + { + if (setRecord.key != _player.key) continue; + var currentValue = Convert.ToSingle(setRecord.value["value"]); + if (currentValue >= 100) continue; + var initialShellPosition = transform.position; + initialShellPosition.y += 10; + Instantiate(shell, initialShellPosition, Quaternion.LookRotation(Vector3.down)); - } + m_CurrentHealth = Convert.ToSingle(currentValue); + SetHealthUI(); + } - // TODO: Callback for HealthTable deletion - private void OnPlayerDeath(HealthTableUpdate update) - { - { - if (update.Key != _player.key) return; + foreach (var removedRecord in update.RemovedRecords) + { + OnDeath(); + } + } - if (update.Value.Item1 == null) - { - OnDeath(); - return; - } - } - } + private void SetHealthUI() + { + // Adjust the value and colour of the slider. + m_Slider.value = m_CurrentHealth; + m_FillImage.color = Color.Lerp(m_ZeroHealthColor, m_FullHealthColor, m_CurrentHealth / m_StartingHealth); + } - private void SetHealthUI() - { - // Adjust the value and colour of the slider. - m_Slider.value = m_CurrentHealth; - m_FillImage.color = Color.Lerp(m_ZeroHealthColor, m_FullHealthColor, m_CurrentHealth / m_StartingHealth); - } + private void OnDeath() + { + m_Dead = true; + m_ExplosionParticles.transform.position = transform.position; + m_ExplosionParticles.gameObject.SetActive(true); + m_ExplosionParticles.Play(); + gameObject.SetActive(false); + } - private void OnDeath() - { - m_Dead = true; - m_ExplosionParticles.transform.position = transform.position; - m_ExplosionParticles.gameObject.SetActive(true); - m_ExplosionParticles.Play(); - gameObject.SetActive(false); - } - - private void OnDestroy() - { - _disposable?.Dispose(); - } + private void OnDestroy() + { + _disposable?.Dispose(); + } } diff --git a/packages/client/Assets/Scripts/codegen/DamageTable.cs b/packages/client/Assets/Scripts/codegen/DamageTable.cs index 0214125..e79e978 100644 --- a/packages/client/Assets/Scripts/codegen/DamageTable.cs +++ b/packages/client/Assets/Scripts/codegen/DamageTable.cs @@ -18,33 +18,6 @@ public class DamageTable : IMudTable public ulong? value; - public static DamageTable? GetTableValue(string key) - { - var query = new Query() - .Find("?value", "?attribute") - .Where(TableId.ToString(), key, "?attribute", "?value"); - var result = NetworkManager.Instance.ds.Query(query); - var damageTable = new DamageTable(); - 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; - damageTable.value = valueValue; - hasValues = true; - break; - } - } - - return hasValues ? damageTable : null; - } - public static IObservable OnRecordUpdate() { return NetworkManager.Instance.ds.OnDataStoreUpdate diff --git a/packages/client/Assets/Scripts/codegen/HealthTable.cs b/packages/client/Assets/Scripts/codegen/HealthTable.cs index 39c4806..4696412 100644 --- a/packages/client/Assets/Scripts/codegen/HealthTable.cs +++ b/packages/client/Assets/Scripts/codegen/HealthTable.cs @@ -18,33 +18,6 @@ public class HealthTable : IMudTable public ulong? value; - public static HealthTable? GetTableValue(string key) - { - var query = new Query() - .Find("?value", "?attribute") - .Where(TableId.ToString(), key, "?attribute", "?value"); - var result = NetworkManager.Instance.ds.Query(query); - var healthTable = new HealthTable(); - 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; - healthTable.value = valueValue; - hasValues = true; - break; - } - } - - return hasValues ? healthTable : null; - } - public static IObservable OnRecordUpdate() { return NetworkManager.Instance.ds.OnDataStoreUpdate diff --git a/packages/client/Assets/Scripts/codegen/PlayerTable.cs b/packages/client/Assets/Scripts/codegen/PlayerTable.cs index a8181b4..dea54ef 100644 --- a/packages/client/Assets/Scripts/codegen/PlayerTable.cs +++ b/packages/client/Assets/Scripts/codegen/PlayerTable.cs @@ -18,33 +18,6 @@ public class PlayerTable : IMudTable public bool? value; - public static PlayerTable? GetTableValue(string key) - { - var query = new Query() - .Find("?value", "?attribute") - .Where(TableId.ToString(), key, "?attribute", "?value"); - var result = NetworkManager.Instance.ds.Query(query); - var playerTable = new PlayerTable(); - var hasValues = false; - - foreach (var record in result) - { - var attribute = record["attribute"].ToString(); - var value = record["value"]; - - switch (attribute) - { - case "value": - var valueValue = (bool)value; - playerTable.value = valueValue; - hasValues = true; - break; - } - } - - return hasValues ? playerTable : null; - } - public static IObservable OnRecordUpdate() { return NetworkManager.Instance.ds.OnDataStoreUpdate diff --git a/packages/client/Assets/Scripts/codegen/PositionTable.cs b/packages/client/Assets/Scripts/codegen/PositionTable.cs index a4c3100..d645e70 100644 --- a/packages/client/Assets/Scripts/codegen/PositionTable.cs +++ b/packages/client/Assets/Scripts/codegen/PositionTable.cs @@ -19,38 +19,6 @@ public class PositionTable : IMudTable public long? x; public long? y; - public static PositionTable? GetTableValue(string key) - { - var query = new Query() - .Find("?value", "?attribute") - .Where(TableId.ToString(), key, "?attribute", "?value"); - var result = NetworkManager.Instance.ds.Query(query); - var positionTable = new PositionTable(); - var hasValues = false; - - foreach (var record in result) - { - var attribute = record["attribute"].ToString(); - var value = record["value"]; - - switch (attribute) - { - case "x": - var xValue = (long)value; - positionTable.x = xValue; - hasValues = true; - break; - case "y": - var yValue = (long)value; - positionTable.y = yValue; - hasValues = true; - break; - } - } - - return hasValues ? positionTable : null; - } - public static IObservable OnRecordUpdate() { return NetworkManager.Instance.ds.OnDataStoreUpdate diff --git a/packages/client/Packages/manifest.json b/packages/client/Packages/manifest.json index 544f7ca..df71cf8 100644 --- a/packages/client/Packages/manifest.json +++ b/packages/client/Packages/manifest.json @@ -1,7 +1,7 @@ { "dependencies": { "com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask", - "com.emergenceland.unimud": "https://github.com/emergenceland/unimud.git?path=Packages/UniMUD", + "com.emergenceland.unimud": "file:/Users/lil/dev/UniMUD-dev/Packages/UniMUD", "com.unity.ai.navigation": "1.1.3", "com.unity.collab-proxy": "2.0.3", "com.unity.feature.development": "1.0.1", diff --git a/packages/client/Packages/packages-lock.json b/packages/client/Packages/packages-lock.json index dbd562a..4711bb2 100644 --- a/packages/client/Packages/packages-lock.json +++ b/packages/client/Packages/packages-lock.json @@ -8,11 +8,10 @@ "hash": "f2773f585e762480c835ac718b82b87cb111e500" }, "com.emergenceland.unimud": { - "version": "https://github.com/emergenceland/unimud.git?path=Packages/UniMUD", + "version": "file:/Users/lil/dev/UniMUD-dev/Packages/UniMUD", "depth": 0, - "source": "git", - "dependencies": {}, - "hash": "0b4859426bc8c550f57e63339979244dfc65c9ba" + "source": "local", + "dependencies": {} }, "com.unity.ai.navigation": { "version": "1.1.3", 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) From 72472284ab7d69b706856050b864913ef770d6be Mon Sep 17 00:00:00 2001 From: lermchair <13699109+lermchair@users.noreply.github.com> Date: Mon, 24 Jul 2023 15:58:51 -0700 Subject: [PATCH 3/3] fix bugs --- README.md | 4 +- packages/client/Assets/Resources/Tank.prefab | 98 +-------- packages/client/Assets/Resources/latest.json | 2 +- packages/client/Assets/Scenes/Main.unity | 194 ++++++++++++++++++ .../client/Assets/Scripts/HealthManager.cs | 67 ++++++ .../Assets/Scripts/HealthManager.cs.meta | 3 + .../client/Assets/Scripts/PlayerManager.cs | 1 - packages/client/Assets/Scripts/TankHealth.cs | 38 +--- packages/client/Packages/manifest.json | 2 +- packages/client/Packages/packages-lock.json | 7 +- 10 files changed, 283 insertions(+), 133 deletions(-) create mode 100644 packages/client/Assets/Scripts/HealthManager.cs create mode 100644 packages/client/Assets/Scripts/HealthManager.cs.meta 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 114d673..f968c81 100644 --- a/packages/client/Assets/Resources/Tank.prefab +++ b/packages/client/Assets/Resources/Tank.prefab @@ -684,7 +684,6 @@ Transform: m_Children: - {fileID: 5863183790166139544} - {fileID: 2819717444226315097} - - {fileID: 391825217966097094} m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -768,6 +767,7 @@ 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 @@ -865,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 4b7723f..5bcaa14 100644 --- a/packages/client/Assets/Resources/latest.json +++ b/packages/client/Assets/Resources/latest.json @@ -1,4 +1,4 @@ { "worldAddress": "0x5FbDB2315678afecb367f032d93F642f64180aa3", - "blockNumber": 12 + "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 8ee4be4..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 @@ -1610,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/PlayerManager.cs b/packages/client/Assets/Scripts/PlayerManager.cs index f512db2..8c506f0 100644 --- a/packages/client/Assets/Scripts/PlayerManager.cs +++ b/packages/client/Assets/Scripts/PlayerManager.cs @@ -5,7 +5,6 @@ using mud.Client; using mud.Network.schemas; using mud.Unity; -using Newtonsoft.Json; using UniRx; using UnityEngine; using ObservableExtensions = UniRx.ObservableExtensions; diff --git a/packages/client/Assets/Scripts/TankHealth.cs b/packages/client/Assets/Scripts/TankHealth.cs index db8cd23..6c37e3c 100644 --- a/packages/client/Assets/Scripts/TankHealth.cs +++ b/packages/client/Assets/Scripts/TankHealth.cs @@ -21,7 +21,7 @@ public class TankHealth : MonoBehaviour public Color m_ZeroHealthColor = Color.red; public GameObject m_ExplosionPrefab; public GameObject shell; - private PlayerSync _player; + public PlayerSync _player; private ParticleSystem m_ExplosionParticles; private bool m_Dead; private CompositeDisposable _disposable = new(); @@ -34,11 +34,6 @@ private void Awake() m_ExplosionParticles = Instantiate(m_ExplosionPrefab).GetComponent(); m_ExplosionParticles.gameObject.SetActive(false); _player = GetComponent(); - net = NetworkManager.Instance; - var healthTable = new TableId("", "Health"); - var healthUpdated = new Query().In(healthTable); - var sub = ObservableExtensions.Subscribe(net.ds.RxQuery(healthUpdated).ObserveOnMainThread(), OnHealthChange); - _disposable.Add(sub); } @@ -49,29 +44,14 @@ private void OnEnable() SetHealthUI(); } - - // TODO: Callback for HealthTable update - private void OnHealthChange((List SetRecords, List RemovedRecords) update) + + public void SetHealth(float health) { - Debug.Log("SET: " + JsonConvert.SerializeObject(update.SetRecords)); - Debug.Log("REMOVED: " + JsonConvert.SerializeObject(update.RemovedRecords)); - foreach (var setRecord in update.SetRecords) - { - if (setRecord.key != _player.key) continue; - var currentValue = Convert.ToSingle(setRecord.value["value"]); - if (currentValue >= 100) continue; - var initialShellPosition = transform.position; - initialShellPosition.y += 10; - Instantiate(shell, initialShellPosition, Quaternion.LookRotation(Vector3.down)); - - m_CurrentHealth = Convert.ToSingle(currentValue); - SetHealthUI(); - } - - foreach (var removedRecord in update.RemovedRecords) - { - OnDeath(); - } + m_CurrentHealth = health; + var initialShellPosition = transform.position; + initialShellPosition.y += 10; + Instantiate(shell, initialShellPosition, Quaternion.LookRotation(Vector3.down)); + SetHealthUI(); } private void SetHealthUI() @@ -81,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/Packages/manifest.json b/packages/client/Packages/manifest.json index df71cf8..544f7ca 100644 --- a/packages/client/Packages/manifest.json +++ b/packages/client/Packages/manifest.json @@ -1,7 +1,7 @@ { "dependencies": { "com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask", - "com.emergenceland.unimud": "file:/Users/lil/dev/UniMUD-dev/Packages/UniMUD", + "com.emergenceland.unimud": "https://github.com/emergenceland/unimud.git?path=Packages/UniMUD", "com.unity.ai.navigation": "1.1.3", "com.unity.collab-proxy": "2.0.3", "com.unity.feature.development": "1.0.1", diff --git a/packages/client/Packages/packages-lock.json b/packages/client/Packages/packages-lock.json index 4711bb2..e44199a 100644 --- a/packages/client/Packages/packages-lock.json +++ b/packages/client/Packages/packages-lock.json @@ -8,10 +8,11 @@ "hash": "f2773f585e762480c835ac718b82b87cb111e500" }, "com.emergenceland.unimud": { - "version": "file:/Users/lil/dev/UniMUD-dev/Packages/UniMUD", + "version": "https://github.com/emergenceland/unimud.git?path=Packages/UniMUD", "depth": 0, - "source": "local", - "dependencies": {} + "source": "git", + "dependencies": {}, + "hash": "e5bc8bf178b1f1b294b5e06ee7a873c8c6fe2b37" }, "com.unity.ai.navigation": { "version": "1.1.3",