From a0317259449ff0ae21e752bf8c495d7a93f5f3be Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Mon, 20 Sep 2021 13:40:58 +0200 Subject: [PATCH 01/71] Add component structs --- .../RacingControlComponent.cs | 44 ++++++++++++++-- .../ScriptedActivityComponent.cs | 51 ++++++++++++------- 2 files changed, 73 insertions(+), 22 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 678ad526..07fb18e5 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -1,4 +1,5 @@ using RakDotNet.IO; +using Uchu.Core; namespace Uchu.World { @@ -8,14 +9,49 @@ public class RacingControlComponent : ScriptedActivityComponent public override void Construct(BitWriter writer) { - Serialize(writer); + this.Serialize(writer); } public override void Serialize(BitWriter writer) { - base.Serialize(writer); + // First write scripted activity info (list of players) + // Atm this is done in GetConstructPacket here, + // it could be done with base.Construct but then the + // RacingControlSerialization struct isn't actually correct + // base.Construct(writer); + StructPacketParser.WritePacket(this.GetConstructPacket(), writer); + } + + public new RacingControlSerialization GetConstructPacket() + { + var packet = this.GetPacket(); + packet.ActivityUserInfos = new ActivityUserInfo[Participants.Count]; + for (var i = 0; i < this.Participants.Count; i++) + { + var participant = this.Participants[i]; + var parameters = Parameters[i]; + var activityUserInfo = new ActivityUserInfo + { + User = participant, + ActivityValue0 = parameters[0], + ActivityValue1 = parameters[1], + ActivityValue2 = parameters[2], + ActivityValue3 = parameters[3], + ActivityValue4 = parameters[4], + ActivityValue5 = parameters[5], + ActivityValue6 = parameters[6], + ActivityValue7 = parameters[7], + ActivityValue8 = parameters[8], + ActivityValue9 = parameters[9], + }; + packet.ActivityUserInfos[i] = activityUserInfo; + } + + packet.ExpectedPlayerCount = 2; // TODO + + // TODO - // TODO: Look into to + return packet; } } -} \ No newline at end of file +} diff --git a/Uchu.World/Objects/Components/ReplicaComponents/ScriptedActivityComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/ScriptedActivityComponent.cs index 1d171745..5593bc14 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/ScriptedActivityComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/ScriptedActivityComponent.cs @@ -10,7 +10,7 @@ namespace Uchu.World { - public class ScriptedActivityComponent : ReplicaComponent + public class ScriptedActivityComponent : StructReplicaComponent { private readonly Random _random; @@ -217,30 +217,45 @@ public void SetParameter(Player player, int index, float value) } /// - /// Writes the Construct information. + /// Creates the Construct packet for the replica component. /// - /// Writer to write to. - public override void Construct(BitWriter writer) + /// The Construct packet for the replica component. + public override ScriptedActivitySerialization GetConstructPacket() { - Serialize(writer); + var packet = base.GetConstructPacket(); + packet.ActivityUserInfos = new ActivityUserInfo[Participants.Count]; + for (var i = 0; i < this.Participants.Count; i++) + { + var participant = this.Participants[i]; + var parameters = Parameters[i]; + var activityUserInfo = new ActivityUserInfo + { + User = participant, + ActivityValue0 = parameters[0], + ActivityValue1 = parameters[1], + ActivityValue2 = parameters[2], + ActivityValue3 = parameters[3], + ActivityValue4 = parameters[4], + ActivityValue5 = parameters[5], + ActivityValue6 = parameters[6], + ActivityValue7 = parameters[7], + ActivityValue8 = parameters[8], + ActivityValue9 = parameters[9], + }; + packet.ActivityUserInfos[i] = activityUserInfo; + } + + return packet; } /// - /// Writes the Serialize information. + /// Creates the Serialize packet for the replica component. /// - /// Writer to write to. - public override void Serialize(BitWriter writer) + /// The Serialize packet for the replica component. + public override ScriptedActivitySerialization GetSerializePacket() { - writer.WriteBit(true); - writer.Write((uint) Participants.Count); - - foreach (var contributor in Participants) - { - writer.Write(contributor); - - foreach (var parameter in Parameters[this.Participants.IndexOf(contributor)]) - writer.Write(parameter); - } + var packet = this.GetConstructPacket(); + return packet; } } } From e6c164955f3e2c63d218009f0ca9315fe87dd1e9 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Fri, 24 Sep 2021 20:27:19 +0200 Subject: [PATCH 02/71] Some experimenting --- .../Struct/Property/StringPacketProperty.cs | 20 +- .../Handlers/GameMessages/GeneralHandler.cs | 2 +- .../GameMessages/ModularBuildingHandler.cs | 16 +- .../Handlers/GameMessages/RacingHandler.cs | 21 ++ Uchu.World/Handlers/PositionUpdateHandler.cs | 6 +- .../ModuleAssemblyComponent.cs | 33 ++- .../ReplicaComponents/PossessableComponent.cs | 9 +- .../RacingControlComponent.cs | 205 +++++++++++++++++- .../ReplicaComponents/ReplicaComponent.cs | 18 +- .../VehiclePhysicsComponent.cs | 178 ++++++++++++++- Uchu.World/Objects/Zone.cs | 4 +- .../Client/ModuleAssemblyQueryDataMessage.cs | 9 + .../Serialize/ModuleAssemblySerialization.cs | 21 ++ .../Serialize/PossessableSerialization.cs | 13 +- .../Serialize/RacingControlSerialization.cs | 50 +++++ .../ScriptedActivitySerialization.cs | 26 +++ .../Systems/Behaviors/CarBoostBehavior.cs | 8 + 17 files changed, 603 insertions(+), 36 deletions(-) create mode 100644 Uchu.World/Handlers/GameMessages/RacingHandler.cs create mode 100644 Uchu.World/Packets/GameMessages/Client/ModuleAssemblyQueryDataMessage.cs create mode 100644 Uchu.World/Packets/Replica/Serialize/ModuleAssemblySerialization.cs create mode 100644 Uchu.World/Packets/Replica/Serialize/RacingControlSerialization.cs create mode 100644 Uchu.World/Packets/Replica/Serialize/ScriptedActivitySerialization.cs create mode 100644 Uchu.World/Systems/Behaviors/CarBoostBehavior.cs diff --git a/Uchu.Core/Serializable/Struct/Property/StringPacketProperty.cs b/Uchu.Core/Serializable/Struct/Property/StringPacketProperty.cs index 379ccde4..89a799bf 100644 --- a/Uchu.Core/Serializable/Struct/Property/StringPacketProperty.cs +++ b/Uchu.Core/Serializable/Struct/Property/StringPacketProperty.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Reflection; using RakDotNet.IO; @@ -20,6 +21,11 @@ public class StringPacketProperty : IPacketProperty /// Whether the encoded string is "wide" (2 bytes per character). /// private bool _isWide = false; + + /// + /// Type of the property that the string length is written. + /// + private Type _stringLengthPropertyType = typeof(uint); /// /// Creates the string packet property. @@ -39,6 +45,11 @@ public StringPacketProperty(PropertyInfo property) { this._isWide = true; } + // Get the string length type. + if (property.GetCustomAttribute(typeof(StoreLengthAsAttribute)) is StoreLengthAsAttribute storeLengthAs) + { + this._stringLengthPropertyType = storeLengthAs.Type; + } } /// @@ -55,7 +66,12 @@ public void Write(object objectToWrite, BitWriter writer, Dictionary((uint) length); + // var test = Convert.ChangeType(length, this._stringLengthPropertyType); + // TODO: do this in a way similar to the Array one instead of only handling these 2 + if (this._stringLengthPropertyType == typeof(ushort)) + writer.Write((ushort) length); + else + writer.Write((uint) length); } writer.WriteString(value, length, this._isWide); @@ -91,4 +107,4 @@ public void Read(object objectToWrite, BitReader reader, Dictionary(out var moduleAssembly)) + Logger.Error($"assembly data requested for {message.Associate} but no module assembly component exists"); + player.Zone.BroadcastMessage(new ModuleAssemblyDBDataForClientMessage + { + AssemblyId = GameObject.InvalidObject, // todo subkey? i think + // chassis wheels engine frontbumper rearpanel rearbumper sides + Blob = moduleAssembly.GetAssembly(), + Associate = message.Associate, + }); + } } -} \ No newline at end of file +} diff --git a/Uchu.World/Handlers/GameMessages/RacingHandler.cs b/Uchu.World/Handlers/GameMessages/RacingHandler.cs new file mode 100644 index 00000000..9fe11bdc --- /dev/null +++ b/Uchu.World/Handlers/GameMessages/RacingHandler.cs @@ -0,0 +1,21 @@ +using Uchu.Core; + +namespace Uchu.World.Handlers.GameMessages +{ + public class RacingHandler : HandlerGroup + { + [PacketHandler] + public void AcknowledgePossessionHandler(AcknowledgePossessionMessage message, Player player) + { + var racingControl = player.Zone.ZoneControlObject.GetComponent(); + racingControl.OnAcknowledgePossession(message); + } + + [PacketHandler] + public void VehicleSetWheelLockStateHandler(VehicleSetWheelLockStateMessage message, Player player) + { + Logger.Information($"Set wheel lock state: friction = {message.ExtraFriction}, locked = {message.Locked}"); + // TODO: handle + } + } +} diff --git a/Uchu.World/Handlers/PositionUpdateHandler.cs b/Uchu.World/Handlers/PositionUpdateHandler.cs index 8141aaae..e8c36b12 100644 --- a/Uchu.World/Handlers/PositionUpdateHandler.cs +++ b/Uchu.World/Handlers/PositionUpdateHandler.cs @@ -13,6 +13,10 @@ public async Task PositionHandler(PositionUpdatePacket packet, IRakConnection co { var player = UchuServer.FindPlayer(connection); if (player?.Transform == default) return; + + Logger.Debug(packet.Position); + + // TODO: set position of possessed object? maybe? // The server is a slave to the position update packets it gets from the client right now. player.Transform.Position = packet.Position; @@ -46,4 +50,4 @@ public async Task PositionHandler(PositionUpdatePacket packet, IRakConnection co await player.OnPositionUpdate.InvokeAsync(packet.Position, packet.Rotation); } } -} \ No newline at end of file +} diff --git a/Uchu.World/Objects/Components/ReplicaComponents/ModuleAssemblyComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/ModuleAssemblyComponent.cs index bd73fd58..b3d47ee6 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/ModuleAssemblyComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/ModuleAssemblyComponent.cs @@ -1,18 +1,39 @@ -using RakDotNet.IO; namespace Uchu.World { - public class ModuleAssemblyComponent : ReplicaComponent + public class ModuleAssemblyComponent : StructReplicaComponent { public override ComponentId Id => ComponentId.ModuleAssemblyComponent; - public override void Construct(BitWriter writer) + private string _parts; + + public void SetAssembly(string parts) + { + this._parts = parts; + } + + public string GetAssembly() { - writer.WriteBit(false); + return this._parts; } - public override void Serialize(BitWriter writer) + public override ModuleAssemblySerialization GetConstructPacket() { + var packet = base.GetConstructPacket(); + packet.ModuleAssemblyInfo = new ModuleAssemblyInfo + { + Assembly = GameObject.InvalidObject, // this.GameObject ? subkey ? + Blob = this._parts, + UseOptionalParts = false, + }; + return packet; } + + public override ModuleAssemblySerialization GetSerializePacket() + { + var packet = this.GetConstructPacket(); + return packet; + } + } -} \ No newline at end of file +} diff --git a/Uchu.World/Objects/Components/ReplicaComponents/PossessableComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/PossessableComponent.cs index 67be1fbe..bf181680 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/PossessableComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/PossessableComponent.cs @@ -13,9 +13,12 @@ public class PossessableComponent : StructReplicaComponent ComponentId.RacingControlComponent; + private List _preRacePlayerInfos = new List(); + private List _duringRacePlayerInfos = new List(); + + private RaceInfo _raceInfo = new RaceInfo(); + + public RacingControlComponent() + { + _raceInfo.LapCount = 3; + _raceInfo.PathName = "MainPath"; // MainPath + Listen(OnStart, async () => + { + await LoadAsync(); + }); + } + + private async Task LoadAsync() + { + Listen(this.GameObject.OnMessageBoxRespond, (player, message) => + { + Logger.Information($"Button - {message.Button} {message.Identifier} {message.UserData}"); + if (message.Identifier == "ACT_RACE_EXIT_THE_RACE?" && message.Button == 1) + { + // TODO: Player wants to leave race + } + }); + + Listen(Zone.OnPlayerLoad, player => + { + Listen(player.OnWorldLoad, async () => + { + Logger.Information("Racing Control Component"); + // Entity::GameObject * playerObject = owner->GetZoneInstance()->objectsManager->GetObjectByID(playerID); + // -> player + + + // // Set ObjectID + // myCar->SetObjectID(owner->GetZoneInstance()->objectsManager->GenerateSpawnedID()); + // -> handled automatically + + // // Set Position/Rotation + // auto path = reinterpret_cast(owner->GetZoneInstance()->luZone->paths.find(u"MainPath")->second); + // myCar->SetPosition(path->waypoints.at(0)->position); + var mainPath = Zone.ZoneInfo.LuzFile.PathData.FirstOrDefault(path => path.PathName == "MainPath"); + if (mainPath == default) + throw new Exception("Path not found"); + + var startPosition = mainPath.Waypoints.First().Position; + + // // Make car + // Entity::GameObject* myCar = new Entity::GameObject(this->owner->GetZoneInstance(), 8092); + // if (!myCar->isSerializable) { + // Spawn Error Object + // delete myCar; + // myCar = new Entity::GameObject(this->owner->GetZoneInstance(), 1845); + GameObject car = GameObject.Instantiate(this.GameObject.Zone.ZoneControlObject, 8092, startPosition + new Vector3(20, 50, 0)); + // car.Transform.Position = startPosition; + + player.Teleport(startPosition + new Vector3(20, 50, 0)); + + // auto modAComp = myCar->GetComponent(); + var moduleAssemblyComponent = car.GetComponent(); + // modAComp->SetAssembly(u"1:8129;1:8130;1:13513;1:13512;1:13515;1:13516;1:13514;"); + moduleAssemblyComponent.SetAssembly("1:8129;1:8130;1:14709;1:14708;1:14711;1:14712;1:14710;"); + + // // Set Parent + // myCar->SetParent(this->owner->GetZoneInstance()->zoneControlObject); + // + // // Finish & Send car + // myCar->Finish(); + // + // // Register + // this->owner->GetZoneInstance()->objectsManager->RegisterObject(myCar); + // + // // Construct + // if (myCar->isSerializable) + // this->owner->GetZoneInstance()->objectsManager->Construct(myCar); + + Start(car); + GameObject.Construct(car); + + // // Add player to activity + // this->AddPlayerToActivity(playerID); + AddPlayer(player); + + + // await UiHelper.StateAsync(player, "Race"); + + var preInfo = new PreRacePlayerInfo + { + IsReady = true, + PlayerId = player, + StartingPosition = 0, + VehicleId = car, + }; + this._preRacePlayerInfos.Add(preInfo); + var duringInfo = new DuringRacePlayerInfo + { + PlayerId = player, + BestLapTime = 7.24f, //testing, doesnt appear on client so far + RaceTime = 8.97f, //testing, doesnt appear on client so far + }; + this._duringRacePlayerInfos.Add(duringInfo); + + // // Tell player car is ready and added to race + // this->owner->GetZoneInstance()->objectsManager->Serialize(this->owner); + GameObject.Serialize(this.GameObject); + + // playerObject->Possess(myCar); + car.GetComponent().Driver = player; + player.GetComponent().VehicleObject = car; + + // { GM::NotifyVehicleOfRacingObject msg; msg.racingObjectID = this->owner->GetObjectID(); GameMessages::Broadcast(this->owner->GetZoneInstance(), myCar, msg); } + Zone.BroadcastMessage(new NotifyVehicleOfRacingObjectMessage + { + Associate = car, + RacingObjectId = this.GameObject, // zonecontrol + }); + // { GM::RacingPlayerLoaded msg; msg.playerID = playerID; msg.vehicleID = myCar->GetObjectID(); GameMessages::Broadcast(owner->GetZoneInstance(), owner, msg); } + Zone.BroadcastMessage(new RacingPlayerLoadedMessage + { + Associate = this.GameObject, + PlayerId = player, + VehicleId = car, + }); + // {GM::VehicleUnlockInput msg; msg.bLockWheels = false; GameMessages::Broadcast(owner->GetZoneInstance(), myCar, msg); } + Zone.BroadcastMessage(new VehicleUnlockInputMessage + { + Associate = car, + LockWheels = false, + }); + + GameObject.Serialize(car); + GameObject.Serialize(player); + + //test + Zone.Schedule(() => + { + Logger.Information("delay thingy"); + car.Transform.Position = new Vector3(0, 250, 0); + GameObject.Serialize(car); + Zone.SendSerialization(car, new []{ player }); + }, 10000); + }); + }); + } + + // todo + // RacingClientReady client->server + // RacingPlayerLoaded server->client + // RacingResetPlayerToLastReset server->client --> this is like player reached respawn checkpoint + // RacingSetPlayerResetInfo server->client + // RacingPlayerInfoResetFinished client->server + + // in capture even before acknowledge poss: + // teleport msg [player] + // bIgnoreY = False + // bSetRotation = True + // bSkipAllChecks = False + // pos = (-1457.711669921875, 794.0, -351.61419677734375) + // useNavmesh = False + // w = 0.5037656426429749 + // x = 0.0 + // y = 0.8638404011726379 + // z = 0.0 + + public void OnAcknowledgePossession(AcknowledgePossessionMessage message) + { + GameObject.Serialize(this.GameObject); + + Zone.BroadcastMessage(new NotifyRacingClientMessage + { + Associate = this.GameObject, + EventType = RacingClientNotificationType.ActivityStart, + }); + + Zone.BroadcastMessage(new ActivityStartMessage + { + Associate = this.GameObject, + }); + } + public override void Construct(BitWriter writer) { this.Serialize(writer); } + // override to be able to use ScriptedActivityComponent as base public override void Serialize(BitWriter writer) { // First write scripted activity info (list of players) @@ -25,11 +216,16 @@ public override void Serialize(BitWriter writer) public new RacingControlSerialization GetConstructPacket() { var packet = this.GetPacket(); - packet.ActivityUserInfos = new ActivityUserInfo[Participants.Count]; + packet.ActivityUserInfos = new ActivityUserInfo[this.Participants.Count]; + Logger.Information("Adding participants to packet"); for (var i = 0; i < this.Participants.Count; i++) { + Logger.Information($"Adding participant {i}"); var participant = this.Participants[i]; var parameters = Parameters[i]; + parameters[0] = 1; + parameters[1] = 265f; // doesn't appear on client + parameters[2] = 87f; // doesn't appear on client var activityUserInfo = new ActivityUserInfo { User = participant, @@ -47,7 +243,12 @@ public override void Serialize(BitWriter writer) packet.ActivityUserInfos[i] = activityUserInfo; } - packet.ExpectedPlayerCount = 2; // TODO + packet.ExpectedPlayerCount = 1; // this displays correctly on the client + + packet.RaceInfo = _raceInfo; // lap count displays correctly on the client + + // packet.PreRacePlayerInfos = this._preRacePlayerInfos.ToArray(); + packet.DuringRacePlayerInfos = this._duringRacePlayerInfos.ToArray(); // TODO diff --git a/Uchu.World/Objects/Components/ReplicaComponents/ReplicaComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/ReplicaComponent.cs index a4440f68..8a3464fe 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/ReplicaComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/ReplicaComponent.cs @@ -10,14 +10,14 @@ public abstract class ReplicaComponent : Component { public static readonly int[] ComponentOrder = { - 108, - 61, - 1, - 3, - 20, - 30, - 40, - 7, + 108, // Possesable + 61, // ModuleAssemblyComponent + 1, // ControllablePhysicsComponent + 3, // SimplePhysicsComponent + 20, // RigidBodyPhantomPhysicsComponent + 30, // VehiclePhysicsComponent + 40, // PhantomPhysicsComponent + 7, // DestructibleComponent 23, 26, 4, @@ -97,4 +97,4 @@ public static Type GetReplica(ComponentId id) /// public abstract void Serialize(BitWriter writer); } -} \ No newline at end of file +} diff --git a/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs index dc99ea54..ab0b4ad8 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs @@ -1,24 +1,194 @@ +using System.Numerics; using RakDotNet.IO; +using Uchu.Core; namespace Uchu.World { - public class VehiclePhysicsComponent : ReplicaComponent + public class VehiclePhysicsComponent : StructReplicaComponent { public override ComponentId Id => ComponentId.VehiclePhysicsComponent; public byte ControlScheme { get; set; } = 5; - + + // this all does not work i think + + /* public override void Construct(BitWriter writer) { + Logger.Information("Construct Vehicle Physics"); + // from VehiclePhysicsComponent.hpp in OpCrux + writer.WriteBit(true); // flag + writer.Write(new Vector3(0, 300, 0)); // writer.Write(this.GameObject.Transform.Position); + writer.Write(this.GameObject.Transform.Rotation); + writer.WriteBit(true); // ground + writer.WriteBit(false); // rail + writer.WriteBit(true); // lin veloc + writer.Write(Vector3.Zero); + writer.WriteBit(true); // angular veloc + writer.Write(Vector3.Zero); + writer.WriteBit(false); // local space info + writer.WriteBit(false); // remote input info + writer.Write(0.0f); // ping + // // serialilze: write false + + // construct: write writer.Write(ControlScheme); writer.WriteBit(true); + writer.WriteBit(false); - Serialize(writer); + // writer.Write(70);//NO + // writer.WriteBit(true); // flag + // writer.Write(this.GameObject.Transform.Position); + // writer.Write(this.GameObject.Transform.Rotation); + // writer.WriteBit(true); // ground + // writer.WriteBit(false); // rail + // writer.WriteBit(true); // lin veloc + // writer.Write(Vector3.Zero); + // writer.WriteBit(true); // angular veloc + // writer.Write(Vector3.Zero); + // writer.WriteBit(false); // local space info + // writer.WriteBit(false); // remote input info + // writer.Write(0.0f); // ping + // serialilze: write false + + // writer.WriteBit(false); + // Serialize(writer); } public override void Serialize(BitWriter writer) { + Logger.Information("Serialize Vehicle Physics"); + // writer.WriteBit(false); + // return; + // from VehiclePhysicsComponent.hpp in OpCrux + writer.WriteBit(true); // flag + writer.Write(new Vector3(0, 300, 0)); // writer.Write(this.GameObject.Transform.Position); + writer.Write(this.GameObject.Transform.Rotation); + writer.WriteBit(true); // ground + writer.WriteBit(false); // rail + writer.WriteBit(true); // lin veloc + writer.Write(Vector3.Zero); + writer.WriteBit(true); // angular veloc + writer.Write(Vector3.Zero); + writer.WriteBit(false); // local space info + writer.WriteBit(false); // remote input info + writer.Write(0.0f); // ping + // serialilze: write false + writer.WriteBit(true); + + // construct: write + // writer.Write(ControlScheme); + // writer.WriteBit(true); + writer.WriteBit(false); + // Serialize(writer); + } +*/ + public override VehiclePhysicsConstruction GetConstructPacket() + { + var packet = base.GetConstructPacket(); + packet.VehicleFrameStats = new VehicleFrameStats + { + Position = new Vector3(300, 300, 0), + Rotation = this.GameObject.Transform.Rotation, + AngularVelocity = Vector3.Zero, + LinearVelocity = Vector3.Zero, + IsOnGround = true, + IsOnRail = false, + RemoteInputPing = 0.0f, + }; + packet.EndOfRaceBehaviorType = (EndOfRaceBehaviorType) 5; + packet.IsInputLocked = true; + return packet; } + + public override VehiclePhysicsSerialization GetSerializePacket() + { + var packet = base.GetSerializePacket(); + Logger.Information(this.GameObject.Transform.Position); + var teleportInfo = new VehicleFrameStatsTeleportInfo + { + VehicleFrameStats = new VehicleFrameStats + { + Position = this.GameObject.Transform.Position, // Vector3(300, 300, 0), + Rotation = this.GameObject.Transform.Rotation, + AngularVelocity = Vector3.Zero, + LinearVelocity = Vector3.Zero, + IsOnGround = true, + IsOnRail = false, + RemoteInputPing = 0.0f, + }, + IsTeleporting = true, + }; + packet.VehicleFrameStatsTeleportInfo = teleportInfo; + return packet; + } + } + + public struct VehiclePhysicsConstruction { + [Default] + public VehicleFrameStats VehicleFrameStats { get; set; } + // write as byte ! + public EndOfRaceBehaviorType EndOfRaceBehaviorType { get; set; } + public bool IsInputLocked { get; set; } + public bool WheelLockExtraFriction { get; set; } + } + + public struct VehiclePhysicsSerialization { + [Default] + public VehicleFrameStatsTeleportInfo VehicleFrameStatsTeleportInfo { get; set; } + // [Default] bool with flag? should this be here + public bool WheelLockExtraFriction { get; set; } + } + + [Struct] + public struct VehicleFrameStats + { + public Vector3 Position { get; set; } + public Quaternion Rotation { get; set; } + public bool IsOnGround { get; set; } + public bool IsOnRail { get; set; } + [Default] + public Vector3 LinearVelocity { get; set; } + [Default] + public Vector3 AngularVelocity { get; set; } + [Default] + public LocalSpaceInfo LocalSpaceInfo { get; set; } + [Default] + public RemoteInputInfo RemoteInputInfo { get; set; } + public float RemoteInputPing { get; set; } + } + + [Struct] + public struct LocalSpaceInfo { + public GameObject ObjectId { get; set; } + public Vector3 Position { get; set; } + [Default] + public Vector3 LinearVelocity { get; set; } + } + + [Struct] + public struct RemoteInputInfo { + public float RemoteInputX { get; set; } + public float RemoteInputY { get; set; } + public bool DoPowerslide { get; set; } + public bool IsModified { get; set; } + } + + [Struct] + public struct VehicleFrameStatsTeleportInfo { + public VehicleFrameStats VehicleFrameStats { get; set; } + public bool IsTeleporting { get; set; } + } + + public enum EndOfRaceBehaviorType : byte { + DriveStraight, + StopStraight, + SlideLeft, + SlideRight, + Do360Left, + Do360Right, + TwoWheels, + Jump, } -} \ No newline at end of file +} diff --git a/Uchu.World/Objects/Zone.cs b/Uchu.World/Objects/Zone.cs index f49dc1cc..4ca3d8d6 100644 --- a/Uchu.World/Objects/Zone.cs +++ b/Uchu.World/Objects/Zone.cs @@ -456,7 +456,7 @@ internal static void SendConstruction(GameObject gameObject, IEnumerable { foreach (var recipient in recipients) { - if (gameObject != gameObject.Zone.ZoneControlObject && !recipient.Perspective.View(gameObject)) continue; + // if (gameObject != gameObject.Zone.ZoneControlObject && !recipient.Perspective.View(gameObject)) continue; if (!recipient.Perspective.Reveal(gameObject, out var id)) continue; if (id == 0) return; @@ -472,7 +472,7 @@ internal static void SendSerialization(GameObject gameObject, IEnumerable GameMessageId.ModuleAssemblyQueryData; + } +} diff --git a/Uchu.World/Packets/Replica/Serialize/ModuleAssemblySerialization.cs b/Uchu.World/Packets/Replica/Serialize/ModuleAssemblySerialization.cs new file mode 100644 index 00000000..db8a8956 --- /dev/null +++ b/Uchu.World/Packets/Replica/Serialize/ModuleAssemblySerialization.cs @@ -0,0 +1,21 @@ +using Uchu.Core; + +namespace Uchu.World +{ + public struct ModuleAssemblySerialization + { + [Default] + public ModuleAssemblyInfo ModuleAssemblyInfo { get; set; } + } + + [Struct] + public struct ModuleAssemblyInfo + { + [Default] + public GameObject Assembly { get; set; } + public bool UseOptionalParts { get; set; } + [Wide] + [StoreLengthAs(typeof(ushort))] + public string Blob { get; set; } + } +} diff --git a/Uchu.World/Packets/Replica/Serialize/PossessableSerialization.cs b/Uchu.World/Packets/Replica/Serialize/PossessableSerialization.cs index e8fd46c7..4cbc8088 100644 --- a/Uchu.World/Packets/Replica/Serialize/PossessableSerialization.cs +++ b/Uchu.World/Packets/Replica/Serialize/PossessableSerialization.cs @@ -4,14 +4,17 @@ namespace Uchu.World { public struct PossessableSerialization { - public bool PossessableInfoExists { get; set; } [Default] - [Requires("PossessableInfoExists")] + public PossessableInfo PossessableInfo { get; set; } + } + + [Struct] + public struct PossessableInfo + { + [Default] public GameObject Driver { get; set; } - [Requires("PossessableInfoExists")] [Default] public uint Animation { get; set; } - [Requires("PossessableInfoExists")] public bool ImmediateDepossess { get; set; } } -} \ No newline at end of file +} diff --git a/Uchu.World/Packets/Replica/Serialize/RacingControlSerialization.cs b/Uchu.World/Packets/Replica/Serialize/RacingControlSerialization.cs new file mode 100644 index 00000000..02cae6fc --- /dev/null +++ b/Uchu.World/Packets/Replica/Serialize/RacingControlSerialization.cs @@ -0,0 +1,50 @@ +using Uchu.Core; + +namespace Uchu.World +{ + [Struct] + public struct PreRacePlayerInfo { + public GameObject PlayerId { get; set; } + public GameObject VehicleId { get; set; } + public uint StartingPosition { get; set; } + public bool IsReady { get; set; } + } + + [Struct] + public struct PostRacePlayerInfo { + public GameObject PlayerId { get; set; } + public uint CurrentRank { get; set; } + } + + [Struct] + public struct DuringRacePlayerInfo { + public GameObject PlayerId { get; set; } + public float BestLapTime { get; set; } + public float RaceTime { get; set; } + } + + [Struct] + public struct RaceInfo { + public ushort LapCount { get; set; } + [Wide] + [StoreLengthAs(typeof(ushort))] + public string PathName { get; set; } + } + + public struct RacingControlSerialization + { + [Default] + [StoreLengthAs(typeof(uint))] + public ActivityUserInfo[] ActivityUserInfos { get; set; } + [Default] + public ushort ExpectedPlayerCount { get; set; } + [Default] + public PreRacePlayerInfo[] PreRacePlayerInfos { get; set; } + [Default] + public PostRacePlayerInfo[] PostRacePlayerInfos { get; set; } + [Default] + public RaceInfo RaceInfo { get; set; } // this is still good, at least the LapCount + [Default] + public DuringRacePlayerInfo[] DuringRacePlayerInfos { get; set; } + } +} diff --git a/Uchu.World/Packets/Replica/Serialize/ScriptedActivitySerialization.cs b/Uchu.World/Packets/Replica/Serialize/ScriptedActivitySerialization.cs new file mode 100644 index 00000000..6f855ce3 --- /dev/null +++ b/Uchu.World/Packets/Replica/Serialize/ScriptedActivitySerialization.cs @@ -0,0 +1,26 @@ +using Uchu.Core; + +namespace Uchu.World +{ + public struct ScriptedActivitySerialization + { + [Default] + public ActivityUserInfo[] ActivityUserInfos { get; set; } + } + + [Struct] + public struct ActivityUserInfo + { + public GameObject User { get; set; } + public float ActivityValue0 { get; set; } + public float ActivityValue1 { get; set; } + public float ActivityValue2 { get; set; } + public float ActivityValue3 { get; set; } + public float ActivityValue4 { get; set; } + public float ActivityValue5 { get; set; } + public float ActivityValue6 { get; set; } + public float ActivityValue7 { get; set; } + public float ActivityValue8 { get; set; } + public float ActivityValue9 { get; set; } + } +} diff --git a/Uchu.World/Systems/Behaviors/CarBoostBehavior.cs b/Uchu.World/Systems/Behaviors/CarBoostBehavior.cs new file mode 100644 index 00000000..77f387fb --- /dev/null +++ b/Uchu.World/Systems/Behaviors/CarBoostBehavior.cs @@ -0,0 +1,8 @@ +namespace Uchu.World.Systems.Behaviors +{ + public class CarBoostBehavior : BehaviorBase + { + public override BehaviorTemplateId Id => BehaviorTemplateId.CarBoost; + // TODO: figure out what to do here + } +} From 46404ab0796a5d8131baf38ee7c12886115cbf6c Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Fri, 24 Sep 2021 21:14:39 +0200 Subject: [PATCH 03/71] Fix Instantiate when item is not in Objects table --- Uchu.World/Objects/GameObjects/Item.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Uchu.World/Objects/GameObjects/Item.cs b/Uchu.World/Objects/GameObjects/Item.cs index b5b4cc81..15e3280a 100644 --- a/Uchu.World/Objects/GameObjects/Item.cs +++ b/Uchu.World/Objects/GameObjects/Item.cs @@ -78,12 +78,12 @@ public static async Task Instantiate(GameObject owner, Lot lot, r => r.Componenttype == (int) ComponentId.ItemComponent ); - if (itemTemplate == default || itemRegistryEntry == default) + if (itemRegistryEntry == default) return default; // If no object Id is provided (e.g. for an NPC or at pickup) generate a random one objectId = objectId == default ? ObjectId.Standalone : objectId; - var instance = Instantiate(owner.Zone, itemTemplate.Name, objectId: objectId, lot: lot); + var instance = Instantiate(owner.Zone, itemTemplate?.Name ?? $"Objects_{lot}_name", objectId: objectId, lot: lot); // Set all the standard values instance.Settings = extraInfo ?? new LegoDataDictionary(); From 438b840d45da0b9d99e99ec05be83f10244186c0 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Sun, 26 Sep 2021 14:42:16 +0200 Subject: [PATCH 04/71] Add OnAcknowledgePossession event to GameObject --- Uchu.World/Handlers/GameMessages/RacingHandler.cs | 3 +-- .../Components/ReplicaComponents/RacingControlComponent.cs | 3 ++- Uchu.World/Objects/GameObjects/GameObject.cs | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Uchu.World/Handlers/GameMessages/RacingHandler.cs b/Uchu.World/Handlers/GameMessages/RacingHandler.cs index 9fe11bdc..2b212a71 100644 --- a/Uchu.World/Handlers/GameMessages/RacingHandler.cs +++ b/Uchu.World/Handlers/GameMessages/RacingHandler.cs @@ -7,8 +7,7 @@ public class RacingHandler : HandlerGroup [PacketHandler] public void AcknowledgePossessionHandler(AcknowledgePossessionMessage message, Player player) { - var racingControl = player.Zone.ZoneControlObject.GetComponent(); - racingControl.OnAcknowledgePossession(message); + message.Associate?.OnAcknowledgePossession.Invoke(message.PossessedObjId); } [PacketHandler] diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 983f6dff..64991623 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -124,6 +124,7 @@ private async Task LoadAsync() GameObject.Serialize(this.GameObject); // playerObject->Possess(myCar); + Listen(player.OnAcknowledgePossession, this.OnAcknowledgePossession); car.GetComponent().Driver = player; player.GetComponent().VehicleObject = car; @@ -181,7 +182,7 @@ private async Task LoadAsync() // y = 0.8638404011726379 // z = 0.0 - public void OnAcknowledgePossession(AcknowledgePossessionMessage message) + public void OnAcknowledgePossession(GameObject possessed) { GameObject.Serialize(this.GameObject); diff --git a/Uchu.World/Objects/GameObjects/GameObject.cs b/Uchu.World/Objects/GameObjects/GameObject.cs index cabc82fc..8a0b21a5 100644 --- a/Uchu.World/Objects/GameObjects/GameObject.cs +++ b/Uchu.World/Objects/GameObjects/GameObject.cs @@ -108,6 +108,8 @@ public int GameMasterLevel public Event OnMessageBoxRespond { get; } + public Event OnAcknowledgePossession { get; } + #endregion #region Macro @@ -142,6 +144,8 @@ protected GameObject() OnMessageBoxRespond = new Event(); + OnAcknowledgePossession = new Event(); + Listen(OnStart, () => { foreach (var component in Components.ToArray()) Start(component); From ed20c2cee82a2494e9a45f9e4c5155cce29bc922 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Mon, 1 Nov 2021 20:08:12 +0100 Subject: [PATCH 05/71] Add script for mission 'Build Your Ride!' --- .../NimbusStation/NimbusCarBuildMission.cs | 40 +++++++++++++++++++ .../NimbusStation/NimbusRocketBuildMission.cs | 4 +- .../Player/ModularBuilderComponent.cs | 25 ++++++++---- 3 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 Uchu.StandardScripts/NimbusStation/NimbusCarBuildMission.cs diff --git a/Uchu.StandardScripts/NimbusStation/NimbusCarBuildMission.cs b/Uchu.StandardScripts/NimbusStation/NimbusCarBuildMission.cs new file mode 100644 index 00000000..245fbff7 --- /dev/null +++ b/Uchu.StandardScripts/NimbusStation/NimbusCarBuildMission.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.Linq; +using Uchu.Core; +using Uchu.Core.Resources; +using Uchu.World; +using Uchu.World.Scripting.Native; + +namespace Uchu.StandardScripts.NimbusStation +{ + /// + /// Native implementation of scripts/ai/ns/l_ns_car_modular_build.lua + /// + [ScriptName("l_ns_car_modular_build.lua")] + public class NimbusCarBuildMission : ObjectScript + { + /// + /// Creates the object script. + /// + /// Game object to control with the script. + public NimbusCarBuildMission(GameObject gameObject) : base(gameObject) + { + Listen(Zone.OnPlayerLoad, player => + { + if (!player.TryGetComponent(out var modularBuilderComponent)) return; + if (!player.TryGetComponent(out var missionInventoryComponent)) return; + + // Listen for rockets being built. + Listen(modularBuilderComponent.OnBuildFinished, async (build) => + { + Logger.Debug("OnBuildFinished: " + build.model); + if (build.model == Lot.ModularCar) + { + await missionInventoryComponent.ScriptAsync(939, 8044); + Logger.Debug("MissionFinished"); + } + }); + }); + } + } +} \ No newline at end of file diff --git a/Uchu.StandardScripts/NimbusStation/NimbusRocketBuildMission.cs b/Uchu.StandardScripts/NimbusStation/NimbusRocketBuildMission.cs index 1868f3b7..c1357462 100644 --- a/Uchu.StandardScripts/NimbusStation/NimbusRocketBuildMission.cs +++ b/Uchu.StandardScripts/NimbusStation/NimbusRocketBuildMission.cs @@ -33,10 +33,10 @@ public NimbusRocketBuildMission(GameObject gameObject) : base(gameObject) if (!player.TryGetComponent(out var missionInventoryComponent)) return; // Listen for rockets being built. - Listen(modularBuilderComponent.OnBuildFinished, async (models) => + Listen(modularBuilderComponent.OnBuildFinished, async (build) => { // Complete the task if one of the models is a Nimbus Rocket part. - var isNimbusRocket = models.Any(model => NimbusRocketParts.Contains(model)); + var isNimbusRocket = build.parts.Any(part => NimbusRocketParts.Contains(part)); if (!isNimbusRocket) return; await missionInventoryComponent.ScriptAsync(1178, 9980); }); diff --git a/Uchu.World/Objects/Components/Player/ModularBuilderComponent.cs b/Uchu.World/Objects/Components/Player/ModularBuilderComponent.cs index cf0183b2..ddf91dfa 100644 --- a/Uchu.World/Objects/Components/Player/ModularBuilderComponent.cs +++ b/Uchu.World/Objects/Components/Player/ModularBuilderComponent.cs @@ -22,11 +22,11 @@ public class ModularBuilderComponent : Component /// /// Called when a modular build is completed. /// - public Event OnBuildFinished { get; } + public Event OnBuildFinished { get; } protected ModularBuilderComponent() { - this.OnBuildFinished = new Event(); + this.OnBuildFinished = new Event(); } public async Task StartBuildingAsync(StartBuildingWithItemMessage message) @@ -89,22 +89,26 @@ public async Task StartBuildingWithModel(Item model) { /// /// When the player is done with a modular build and gets a model. /// - public async Task FinishBuilding(Lot[] models) + public async Task FinishBuilding(Lot[] parts) { var inventory = GameObject.GetComponent(); // Remove all the items that were used for building this module - foreach (var lot in models) + foreach (var lot in parts) await inventory.RemoveLotAsync(lot, 1, InventoryType.TemporaryModels); - await CreateNewModel(models); + var modelLot = await CreateNewModel(parts); // Don't give back the original model if the user made a new one. CurrentModel = null; // Finish the build. await ConfirmFinish(); - await this.OnBuildFinished.InvokeAsync(models); + await this.OnBuildFinished.InvokeAsync(new ModularBuildCompleteEvent + { + model = modelLot, + parts = parts + }); } /// @@ -163,7 +167,7 @@ await inventory.MoveItemBetweenInventoriesAsync( IsBuilding = false; } - private async Task CreateNewModel(Lot[] parts) + private async Task CreateNewModel(Lot[] parts) { var inventory = GameObject.GetComponent(); @@ -174,6 +178,8 @@ private async Task CreateNewModel(Lot[] parts) Lot modelLot = GetModelLotForBuildMode(Mode); await inventory.AddLotAsync(modelLot, 1, model, InventoryType.Models); + + return modelLot; } private async Task GetBackOldModel() @@ -235,5 +241,10 @@ private static Lot GetModelLotForBuildMode(BuildMode mode) return default; } } + + public struct ModularBuildCompleteEvent { + public Lot model {get; set;} + public Lot[] parts {get; set;} + } } } From ca4681e2de5a6dd409c70e3543c950a7f8d1702c Mon Sep 17 00:00:00 2001 From: TecCheck Date: Mon, 1 Nov 2021 20:09:15 +0100 Subject: [PATCH 06/71] Remove logs from NimbusCarBuildMission.cs --- Uchu.StandardScripts/NimbusStation/NimbusCarBuildMission.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Uchu.StandardScripts/NimbusStation/NimbusCarBuildMission.cs b/Uchu.StandardScripts/NimbusStation/NimbusCarBuildMission.cs index 245fbff7..35031308 100644 --- a/Uchu.StandardScripts/NimbusStation/NimbusCarBuildMission.cs +++ b/Uchu.StandardScripts/NimbusStation/NimbusCarBuildMission.cs @@ -24,15 +24,11 @@ public NimbusCarBuildMission(GameObject gameObject) : base(gameObject) if (!player.TryGetComponent(out var modularBuilderComponent)) return; if (!player.TryGetComponent(out var missionInventoryComponent)) return; - // Listen for rockets being built. + // Listen for cars being built. Listen(modularBuilderComponent.OnBuildFinished, async (build) => { - Logger.Debug("OnBuildFinished: " + build.model); if (build.model == Lot.ModularCar) - { await missionInventoryComponent.ScriptAsync(939, 8044); - Logger.Debug("MissionFinished"); - } }); }); } From 6a0630cc211a34cbf8c930356843988f651fef2b Mon Sep 17 00:00:00 2001 From: TecCheck Date: Tue, 2 Nov 2021 22:55:41 +0100 Subject: [PATCH 07/71] Fix LDF parsing in game messages --- Uchu.Core/Serializable/Struct/Property/PacketProperty.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Uchu.Core/Serializable/Struct/Property/PacketProperty.cs b/Uchu.Core/Serializable/Struct/Property/PacketProperty.cs index 9b129ae4..77cac1f6 100644 --- a/Uchu.Core/Serializable/Struct/Property/PacketProperty.cs +++ b/Uchu.Core/Serializable/Struct/Property/PacketProperty.cs @@ -62,7 +62,11 @@ public class PacketProperty : IPacketProperty { typeof(LegoDataDictionary), (reader, context) => { - return LegoDataDictionary.FromString(reader.ReadString((int) reader.Read(), true)); + var length = (int) reader.Read(); + var legoDataDictionary = LegoDataDictionary.FromString(reader.ReadString(length, true)); + if (length > 0) reader.Read(); // Trailing null bytes + + return legoDataDictionary; } }, }; From ee4d39d5b0919f1bd0ce3cc35ad2d9aae88276ba Mon Sep 17 00:00:00 2001 From: TecCheck Date: Tue, 9 Nov 2021 16:22:34 +0100 Subject: [PATCH 08/71] Fix for (parts of) racing control component --- .../Struct/Attribute/NoLengthAttribute.cs | 12 +++++ .../Struct/Property/PacketProperty.cs | 13 +++++- .../RacingControlComponent.cs | 45 +++++-------------- .../Serialize/RacingControlSerialization.cs | 6 +-- .../ScriptedActivitySerialization.cs | 1 + 5 files changed, 40 insertions(+), 37 deletions(-) create mode 100644 Uchu.Core/Serializable/Struct/Attribute/NoLengthAttribute.cs diff --git a/Uchu.Core/Serializable/Struct/Attribute/NoLengthAttribute.cs b/Uchu.Core/Serializable/Struct/Attribute/NoLengthAttribute.cs new file mode 100644 index 00000000..526b516b --- /dev/null +++ b/Uchu.Core/Serializable/Struct/Attribute/NoLengthAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Uchu.Core +{ + /// + /// Uses a break-bit for ending the array + /// + [AttributeUsage(AttributeTargets.Property)] + public class NoLengthAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/Uchu.Core/Serializable/Struct/Property/PacketProperty.cs b/Uchu.Core/Serializable/Struct/Property/PacketProperty.cs index 77cac1f6..af362876 100644 --- a/Uchu.Core/Serializable/Struct/Property/PacketProperty.cs +++ b/Uchu.Core/Serializable/Struct/Property/PacketProperty.cs @@ -92,6 +92,8 @@ public class PacketProperty : IPacketProperty /// private Type _arrayLengthPropertyType = typeof(uint); + private bool _arrayNoLenght = false; + /// /// Custom property writer for the type. /// @@ -166,6 +168,9 @@ public PacketProperty(PropertyInfo property) { this._arrayLengthPropertyType = storeLengthAs.Type; } + + if(property.GetCustomAttribute(typeof(NoLengthAttribute)) is NoLengthAttribute noLength) + this._arrayNoLenght = true; } /// @@ -183,7 +188,7 @@ public void Write(object objectToWrite, BitWriter writer, Dictionary(); - packet.ActivityUserInfos = new ActivityUserInfo[this.Participants.Count]; - Logger.Information("Adding participants to packet"); - for (var i = 0; i < this.Participants.Count; i++) + for (var i = 0; i < this.Parameters.Count; i++) { - Logger.Information($"Adding participant {i}"); - var participant = this.Participants[i]; var parameters = Parameters[i]; parameters[0] = 1; parameters[1] = 265f; // doesn't appear on client parameters[2] = 87f; // doesn't appear on client - var activityUserInfo = new ActivityUserInfo - { - User = participant, - ActivityValue0 = parameters[0], - ActivityValue1 = parameters[1], - ActivityValue2 = parameters[2], - ActivityValue3 = parameters[3], - ActivityValue4 = parameters[4], - ActivityValue5 = parameters[5], - ActivityValue6 = parameters[6], - ActivityValue7 = parameters[7], - ActivityValue8 = parameters[8], - ActivityValue9 = parameters[9], - }; - packet.ActivityUserInfos[i] = activityUserInfo; } - packet.ExpectedPlayerCount = 1; // this displays correctly on the client + base.Serialize(writer); + StructPacketParser.WritePacket(this.GetSerializePacket(), writer); + } + + public new RacingControlSerialization GetSerializePacket() + { + var packet = this.GetPacket(); + + packet.ExpectedPlayerCount = (ushort)this.Participants.Count; + + packet.PreRacePlayerInfos = this._preRacePlayerInfos.ToArray(); packet.RaceInfo = _raceInfo; // lap count displays correctly on the client - // packet.PreRacePlayerInfos = this._preRacePlayerInfos.ToArray(); packet.DuringRacePlayerInfos = this._duringRacePlayerInfos.ToArray(); // TODO diff --git a/Uchu.World/Packets/Replica/Serialize/RacingControlSerialization.cs b/Uchu.World/Packets/Replica/Serialize/RacingControlSerialization.cs index 02cae6fc..138cb147 100644 --- a/Uchu.World/Packets/Replica/Serialize/RacingControlSerialization.cs +++ b/Uchu.World/Packets/Replica/Serialize/RacingControlSerialization.cs @@ -33,18 +33,18 @@ public struct RaceInfo { public struct RacingControlSerialization { - [Default] - [StoreLengthAs(typeof(uint))] - public ActivityUserInfo[] ActivityUserInfos { get; set; } [Default] public ushort ExpectedPlayerCount { get; set; } [Default] + [NoLength] public PreRacePlayerInfo[] PreRacePlayerInfos { get; set; } [Default] + [NoLength] public PostRacePlayerInfo[] PostRacePlayerInfos { get; set; } [Default] public RaceInfo RaceInfo { get; set; } // this is still good, at least the LapCount [Default] + [NoLength] public DuringRacePlayerInfo[] DuringRacePlayerInfos { get; set; } } } diff --git a/Uchu.World/Packets/Replica/Serialize/ScriptedActivitySerialization.cs b/Uchu.World/Packets/Replica/Serialize/ScriptedActivitySerialization.cs index 6f855ce3..6acae8ac 100644 --- a/Uchu.World/Packets/Replica/Serialize/ScriptedActivitySerialization.cs +++ b/Uchu.World/Packets/Replica/Serialize/ScriptedActivitySerialization.cs @@ -5,6 +5,7 @@ namespace Uchu.World public struct ScriptedActivitySerialization { [Default] + [StoreLengthAs(typeof(uint))] public ActivityUserInfo[] ActivityUserInfos { get; set; } } From c1dc519a55c6084353dac34f00645d636d0924b8 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Tue, 9 Nov 2021 16:31:19 +0100 Subject: [PATCH 09/71] Use players car for racing --- .../Components/ReplicaComponents/RacingControlComponent.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index dc8eed27..edacb544 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -75,10 +75,11 @@ private async Task LoadAsync() player.Teleport(startPosition + new Vector3(20, 50, 0)); + var carItem = player.GetComponent().FindItem(Lot.ModularCar); // auto modAComp = myCar->GetComponent(); var moduleAssemblyComponent = car.GetComponent(); // modAComp->SetAssembly(u"1:8129;1:8130;1:13513;1:13512;1:13515;1:13516;1:13514;"); - moduleAssemblyComponent.SetAssembly("1:8129;1:8130;1:14709;1:14708;1:14711;1:14712;1:14710;"); + moduleAssemblyComponent.SetAssembly(carItem.Settings["assemblyPartLOTs"].ToString()); // // Set Parent // myCar->SetParent(this->owner->GetZoneInstance()->zoneControlObject); From 45ff9d53e5fe57a070701d118f55bbadd46fdb44 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Sat, 13 Nov 2021 15:04:40 +0100 Subject: [PATCH 10/71] One small step for car(?) This probably includes some unnecessary changes --- .../Components/ReplicaComponents/ModuleAssemblyComponent.cs | 4 +++- .../Components/ReplicaComponents/PossessableComponent.cs | 1 + .../Components/ReplicaComponents/RacingControlComponent.cs | 2 +- .../Components/ReplicaComponents/VehiclePhysicsComponent.cs | 2 +- Uchu.World/Objects/GameObjects/Player.cs | 2 +- Uchu.World/Objects/Zone.cs | 4 ++-- .../Packets/Replica/Serialize/ModuleAssemblySerialization.cs | 3 ++- .../Packets/Replica/Serialize/PossessableSerialization.cs | 4 ++-- 8 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/ModuleAssemblyComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/ModuleAssemblyComponent.cs index b3d47ee6..bbc4f270 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/ModuleAssemblyComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/ModuleAssemblyComponent.cs @@ -23,15 +23,17 @@ public override ModuleAssemblySerialization GetConstructPacket() packet.ModuleAssemblyInfo = new ModuleAssemblyInfo { Assembly = GameObject.InvalidObject, // this.GameObject ? subkey ? - Blob = this._parts, + Blob = "1:8129;1:8130;1:13513;1:13512;1:13515;1:13516;1:13514;", UseOptionalParts = false, }; + packet.Flag = true; return packet; } public override ModuleAssemblySerialization GetSerializePacket() { var packet = this.GetConstructPacket(); + packet.Flag = false; return packet; } diff --git a/Uchu.World/Objects/Components/ReplicaComponents/PossessableComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/PossessableComponent.cs index bf181680..93e07ae2 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/PossessableComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/PossessableComponent.cs @@ -17,6 +17,7 @@ public override PossessableSerialization GetPacket() { Driver = this.Driver, ImmediateDepossess = false, + Temp1 = false, }; return packet; } diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index edacb544..4a3e38cb 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -70,7 +70,7 @@ private async Task LoadAsync() // Spawn Error Object // delete myCar; // myCar = new Entity::GameObject(this->owner->GetZoneInstance(), 1845); - GameObject car = GameObject.Instantiate(this.GameObject.Zone.ZoneControlObject, 8092, startPosition + new Vector3(20, 50, 0)); + GameObject car = GameObject.Instantiate(this.GameObject.Zone.ZoneControlObject, 8092, startPosition + new Vector3(0, 50, 0)); // car.Transform.Position = startPosition; player.Teleport(startPosition + new Vector3(20, 50, 0)); diff --git a/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs index ab0b4ad8..1a9d1090 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs @@ -110,7 +110,7 @@ public override VehiclePhysicsSerialization GetSerializePacket() { VehicleFrameStats = new VehicleFrameStats { - Position = this.GameObject.Transform.Position, // Vector3(300, 300, 0), + Position = new Vector3(300, 300, 0), Rotation = this.GameObject.Transform.Rotation, AngularVelocity = Vector3.Zero, LinearVelocity = Vector3.Zero, diff --git a/Uchu.World/Objects/GameObjects/Player.cs b/Uchu.World/Objects/GameObjects/Player.cs index 634d5b38..411640a1 100644 --- a/Uchu.World/Objects/GameObjects/Player.cs +++ b/Uchu.World/Objects/GameObjects/Player.cs @@ -36,7 +36,7 @@ public static async Task Instantiate(IRakConnection connection, Zone zon // Create base game object var instance = Instantiate( zone, - position: zone.SpawnPosition, + position: new Vector3(300, 300, 0), rotation: zone.SpawnRotation, scale: 1, objectId: id, diff --git a/Uchu.World/Objects/Zone.cs b/Uchu.World/Objects/Zone.cs index bcd9798e..39a81780 100644 --- a/Uchu.World/Objects/Zone.cs +++ b/Uchu.World/Objects/Zone.cs @@ -516,7 +516,7 @@ internal static void SendConstruction(GameObject gameObject, IEnumerable { foreach (var recipient in recipients) { - // if (gameObject != gameObject.Zone.ZoneControlObject && !recipient.Perspective.View(gameObject)) continue; + if (gameObject != gameObject.Zone.ZoneControlObject && !recipient.Perspective.View(gameObject)) continue; if (!recipient.Perspective.Reveal(gameObject, out var id)) continue; if (id == 0) return; @@ -532,7 +532,7 @@ internal static void SendSerialization(GameObject gameObject, IEnumerable Date: Sat, 13 Nov 2021 16:50:33 +0100 Subject: [PATCH 11/71] Implement racing start routine --- .../RacingControlComponent.cs | 268 ++++++++++-------- 1 file changed, 153 insertions(+), 115 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 4a3e38cb..08bf4642 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -21,6 +21,8 @@ public class RacingControlComponent : ScriptedActivityComponent private RaceInfo _raceInfo = new RaceInfo(); + private RacingStatus racingStatus = RacingStatus.None; + public RacingControlComponent() { _raceInfo.LapCount = 3; @@ -44,124 +46,154 @@ private async Task LoadAsync() Listen(Zone.OnPlayerLoad, player => { - Listen(player.OnWorldLoad, async () => + Listen(player.OnWorldLoad, async () => OnPlayerLoad(player)); + }); + } + + private void OnPlayerLoad(Player player) + { + Logger.Information("Player loaded into racing"); + + // Register player + this._preRacePlayerInfos.Add(new PreRacePlayerInfo + { + PlayerId = player, + IsReady = true, + StartingPosition = (uint)_preRacePlayerInfos.Count + 1, + }); + + LoadPlayerCar(player); + Zone.Schedule(InitRace, 10000); + } + + private void LoadPlayerCar(Player player) + { + // Get position and rotation + var mainPath = Zone.ZoneInfo.LuzFile.PathData.FirstOrDefault(path => path.PathName == "MainPath"); + if (mainPath == default) + throw new Exception("Path not found"); + + var startPosition = mainPath.Waypoints.First().Position + Vector3.UnitY * 3; + var spacing = 15; + var range = _preRacePlayerInfos.Count * spacing; + startPosition += Vector3.UnitZ * range; + + var startRotation = Quaternion.CreateFromYawPitchRoll(((float) Math.PI) * -0.5f, 0 , 0); + + // Create car + player.Teleport(startPosition, startRotation); + GameObject car = GameObject.Instantiate(this.GameObject.Zone.ZoneControlObject, 8092, startPosition, startRotation); + + // Setup imagination + var destroyableComponent = car.GetComponent(); + destroyableComponent.MaxImagination = 60; + destroyableComponent.Imagination = 0; + + // Let the player posses the car + // Listen(player.OnAcknowledgePossession, this.OnAcknowledgePossession); + var possessableComponent = car.GetComponent(); + possessableComponent.Driver = player; + var characterComponent = player.GetComponent(); + characterComponent.VehicleObject = car; + + // Get custom parts + var inventory = player.GetComponent(); + var carItem = inventory.FindItem(Lot.ModularCar); + var moduleAssemblyComponent = car.GetComponent(); + if (carItem == null) + moduleAssemblyComponent.SetAssembly("1:8129;1:8130;1:13513;1:13512;1:13515;1:13516;1:13514;"); // Fallback + else + moduleAssemblyComponent.SetAssembly(carItem.Settings["assemblyPartLOTs"].ToString()); + + Start(car); + GameObject.Construct(car); + GameObject.Serialize(player); + + AddPlayer(player); + + Zone.BroadcastMessage(new RacingSetPlayerResetInfoMessage + { + Associate = this.GameObject, + CurrentLap = 0, + FurthestResetPlane = 0, + PlayerId = player, + RespawnPos = startPosition, + UpcomingPlane = 1, + }); + + // TODO Set Jetpack mode? + + Zone.BroadcastMessage(new NotifyVehicleOfRacingObjectMessage + { + Associate = car, + RacingObjectId = this.GameObject, // zonecontrol + }); + + Zone.BroadcastMessage(new VehicleSetWheelLockStateMessage + { + Associate = car, + ExtraFriction = false, + Locked = true + }); + + Zone.BroadcastMessage(new RacingPlayerLoadedMessage + { + Associate = this.GameObject, + PlayerId = player, + VehicleId = car, + }); + + // Register car for player + for (var i = 0; i < _preRacePlayerInfos.Count; i++) + { + PreRacePlayerInfo info = _preRacePlayerInfos[i]; + if (info.PlayerId == player) { - Logger.Information("Racing Control Component"); - // Entity::GameObject * playerObject = owner->GetZoneInstance()->objectsManager->GetObjectByID(playerID); - // -> player - - - // // Set ObjectID - // myCar->SetObjectID(owner->GetZoneInstance()->objectsManager->GenerateSpawnedID()); - // -> handled automatically - - // // Set Position/Rotation - // auto path = reinterpret_cast(owner->GetZoneInstance()->luZone->paths.find(u"MainPath")->second); - // myCar->SetPosition(path->waypoints.at(0)->position); - var mainPath = Zone.ZoneInfo.LuzFile.PathData.FirstOrDefault(path => path.PathName == "MainPath"); - if (mainPath == default) - throw new Exception("Path not found"); - - var startPosition = mainPath.Waypoints.First().Position; - - // // Make car - // Entity::GameObject* myCar = new Entity::GameObject(this->owner->GetZoneInstance(), 8092); - // if (!myCar->isSerializable) { - // Spawn Error Object - // delete myCar; - // myCar = new Entity::GameObject(this->owner->GetZoneInstance(), 1845); - GameObject car = GameObject.Instantiate(this.GameObject.Zone.ZoneControlObject, 8092, startPosition + new Vector3(0, 50, 0)); - // car.Transform.Position = startPosition; - - player.Teleport(startPosition + new Vector3(20, 50, 0)); - - var carItem = player.GetComponent().FindItem(Lot.ModularCar); - // auto modAComp = myCar->GetComponent(); - var moduleAssemblyComponent = car.GetComponent(); - // modAComp->SetAssembly(u"1:8129;1:8130;1:13513;1:13512;1:13515;1:13516;1:13514;"); - moduleAssemblyComponent.SetAssembly(carItem.Settings["assemblyPartLOTs"].ToString()); - - // // Set Parent - // myCar->SetParent(this->owner->GetZoneInstance()->zoneControlObject); - // - // // Finish & Send car - // myCar->Finish(); - // - // // Register - // this->owner->GetZoneInstance()->objectsManager->RegisterObject(myCar); - // - // // Construct - // if (myCar->isSerializable) - // this->owner->GetZoneInstance()->objectsManager->Construct(myCar); - - Start(car); - GameObject.Construct(car); - - // // Add player to activity - // this->AddPlayerToActivity(playerID); - AddPlayer(player); - - - // await UiHelper.StateAsync(player, "Race"); - - var preInfo = new PreRacePlayerInfo - { - IsReady = true, - PlayerId = player, - StartingPosition = 0, - VehicleId = car, - }; - this._preRacePlayerInfos.Add(preInfo); - var duringInfo = new DuringRacePlayerInfo - { - PlayerId = player, - BestLapTime = 7.24f, //testing, doesnt appear on client so far - RaceTime = 8.97f, //testing, doesnt appear on client so far - }; - this._duringRacePlayerInfos.Add(duringInfo); - - // // Tell player car is ready and added to race - // this->owner->GetZoneInstance()->objectsManager->Serialize(this->owner); - GameObject.Serialize(this.GameObject); - - // playerObject->Possess(myCar); - Listen(player.OnAcknowledgePossession, this.OnAcknowledgePossession); - car.GetComponent().Driver = player; - player.GetComponent().VehicleObject = car; - - // { GM::NotifyVehicleOfRacingObject msg; msg.racingObjectID = this->owner->GetObjectID(); GameMessages::Broadcast(this->owner->GetZoneInstance(), myCar, msg); } - Zone.BroadcastMessage(new NotifyVehicleOfRacingObjectMessage - { - Associate = car, - RacingObjectId = this.GameObject, // zonecontrol - }); - // { GM::RacingPlayerLoaded msg; msg.playerID = playerID; msg.vehicleID = myCar->GetObjectID(); GameMessages::Broadcast(owner->GetZoneInstance(), owner, msg); } - Zone.BroadcastMessage(new RacingPlayerLoadedMessage - { - Associate = this.GameObject, - PlayerId = player, - VehicleId = car, - }); - // {GM::VehicleUnlockInput msg; msg.bLockWheels = false; GameMessages::Broadcast(owner->GetZoneInstance(), myCar, msg); } - Zone.BroadcastMessage(new VehicleUnlockInputMessage - { - Associate = car, - LockWheels = false, - }); - - GameObject.Serialize(car); - GameObject.Serialize(player); - - //test - Zone.Schedule(() => - { - Logger.Information("delay thingy"); - car.Transform.Position = new Vector3(0, 250, 0); - GameObject.Serialize(car); - Zone.SendSerialization(car, new []{ player }); - }, 10000); + info.VehicleId = car; + _preRacePlayerInfos[i] = info; + } + } + } + + private void InitRace() + { + if (racingStatus == RacingStatus.Loaded) + return; + + racingStatus = RacingStatus.Loaded; + + Zone.BroadcastMessage(new NotifyRacingClientMessage + { + Associate = this.GameObject, + EventType = RacingClientNotificationType.ActivityStart, + }); + + // Start after 6 seconds + Zone.Schedule(StartRace, 6000); + } + + private void StartRace() + { + if (racingStatus == RacingStatus.Started) + return; + + racingStatus = RacingStatus.Started; + + foreach (PreRacePlayerInfo info in _preRacePlayerInfos) + { + Zone.BroadcastMessage(new VehicleUnlockInputMessage + { + Associate = info.VehicleId, + LockWheels = false, }); + } + + Zone.BroadcastMessage(new ActivityStartMessage + { + Associate = this.GameObject, }); + + GameObject.Serialize(this.GameObject); } // todo @@ -235,5 +267,11 @@ public override void Serialize(BitWriter writer) return packet; } + + private enum RacingStatus { + None, + Loaded, + Started + } } } From bc3afa2dc611b9e154748dde9f65b69b82295ee9 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Sat, 13 Nov 2021 16:58:58 +0100 Subject: [PATCH 12/71] Revert testing changees --- .../ModuleAssemblyComponent.cs | 2 +- .../VehiclePhysicsComponent.cs | 80 +------------------ Uchu.World/Objects/GameObjects/Player.cs | 2 +- 3 files changed, 4 insertions(+), 80 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/ModuleAssemblyComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/ModuleAssemblyComponent.cs index bbc4f270..dccad28e 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/ModuleAssemblyComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/ModuleAssemblyComponent.cs @@ -23,7 +23,7 @@ public override ModuleAssemblySerialization GetConstructPacket() packet.ModuleAssemblyInfo = new ModuleAssemblyInfo { Assembly = GameObject.InvalidObject, // this.GameObject ? subkey ? - Blob = "1:8129;1:8130;1:13513;1:13512;1:13515;1:13516;1:13514;", + Blob = _parts, UseOptionalParts = false, }; packet.Flag = true; diff --git a/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs index 1a9d1090..65396644 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs @@ -8,88 +8,12 @@ public class VehiclePhysicsComponent : StructReplicaComponent ComponentId.VehiclePhysicsComponent; - public byte ControlScheme { get; set; } = 5; - - // this all does not work i think - - /* - public override void Construct(BitWriter writer) - { - Logger.Information("Construct Vehicle Physics"); - // from VehiclePhysicsComponent.hpp in OpCrux - writer.WriteBit(true); // flag - writer.Write(new Vector3(0, 300, 0)); // writer.Write(this.GameObject.Transform.Position); - writer.Write(this.GameObject.Transform.Rotation); - writer.WriteBit(true); // ground - writer.WriteBit(false); // rail - writer.WriteBit(true); // lin veloc - writer.Write(Vector3.Zero); - writer.WriteBit(true); // angular veloc - writer.Write(Vector3.Zero); - writer.WriteBit(false); // local space info - writer.WriteBit(false); // remote input info - writer.Write(0.0f); // ping - // // serialilze: write false - - // construct: write - writer.Write(ControlScheme); - writer.WriteBit(true); - writer.WriteBit(false); - - // writer.Write(70);//NO - // writer.WriteBit(true); // flag - // writer.Write(this.GameObject.Transform.Position); - // writer.Write(this.GameObject.Transform.Rotation); - // writer.WriteBit(true); // ground - // writer.WriteBit(false); // rail - // writer.WriteBit(true); // lin veloc - // writer.Write(Vector3.Zero); - // writer.WriteBit(true); // angular veloc - // writer.Write(Vector3.Zero); - // writer.WriteBit(false); // local space info - // writer.WriteBit(false); // remote input info - // writer.Write(0.0f); // ping - // serialilze: write false - - // writer.WriteBit(false); - // Serialize(writer); - } - - public override void Serialize(BitWriter writer) - { - Logger.Information("Serialize Vehicle Physics"); - // writer.WriteBit(false); - // return; - // from VehiclePhysicsComponent.hpp in OpCrux - writer.WriteBit(true); // flag - writer.Write(new Vector3(0, 300, 0)); // writer.Write(this.GameObject.Transform.Position); - writer.Write(this.GameObject.Transform.Rotation); - writer.WriteBit(true); // ground - writer.WriteBit(false); // rail - writer.WriteBit(true); // lin veloc - writer.Write(Vector3.Zero); - writer.WriteBit(true); // angular veloc - writer.Write(Vector3.Zero); - writer.WriteBit(false); // local space info - writer.WriteBit(false); // remote input info - writer.Write(0.0f); // ping - // serialilze: write false - writer.WriteBit(true); - - // construct: write - // writer.Write(ControlScheme); - // writer.WriteBit(true); - - writer.WriteBit(false); - // Serialize(writer); - } -*/ public override VehiclePhysicsConstruction GetConstructPacket() { var packet = base.GetConstructPacket(); packet.VehicleFrameStats = new VehicleFrameStats { - Position = new Vector3(300, 300, 0), + Position = this.GameObject.Transform.Position, Rotation = this.GameObject.Transform.Rotation, AngularVelocity = Vector3.Zero, LinearVelocity = Vector3.Zero, @@ -110,7 +34,7 @@ public override VehiclePhysicsSerialization GetSerializePacket() { VehicleFrameStats = new VehicleFrameStats { - Position = new Vector3(300, 300, 0), + Position = this.GameObject.Transform.Position, Rotation = this.GameObject.Transform.Rotation, AngularVelocity = Vector3.Zero, LinearVelocity = Vector3.Zero, diff --git a/Uchu.World/Objects/GameObjects/Player.cs b/Uchu.World/Objects/GameObjects/Player.cs index 411640a1..634d5b38 100644 --- a/Uchu.World/Objects/GameObjects/Player.cs +++ b/Uchu.World/Objects/GameObjects/Player.cs @@ -36,7 +36,7 @@ public static async Task Instantiate(IRakConnection connection, Zone zon // Create base game object var instance = Instantiate( zone, - position: new Vector3(300, 300, 0), + position: zone.SpawnPosition, rotation: zone.SpawnRotation, scale: 1, objectId: id, From 76ae70bc4e432f5f91653b19d092bfad6e167531 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Sat, 13 Nov 2021 20:34:01 +0100 Subject: [PATCH 13/71] Merge two lists and change some names --- .../RacingControlComponent.cs | 89 ++++++++++++++----- .../Serialize/RacingControlSerialization.cs | 8 +- 2 files changed, 70 insertions(+), 27 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 08bf4642..4456ca8b 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -16,12 +16,11 @@ public class RacingControlComponent : ScriptedActivityComponent { public override ComponentId Id => ComponentId.RacingControlComponent; - private List _preRacePlayerInfos = new List(); - private List _duringRacePlayerInfos = new List(); + private List _players = new List(); private RaceInfo _raceInfo = new RaceInfo(); - private RacingStatus racingStatus = RacingStatus.None; + private RacingStatus _racingStatus = RacingStatus.None; public RacingControlComponent() { @@ -55,11 +54,11 @@ private void OnPlayerLoad(Player player) Logger.Information("Player loaded into racing"); // Register player - this._preRacePlayerInfos.Add(new PreRacePlayerInfo + this._players.Add(new RacingPlayerInfo { - PlayerId = player, - IsReady = true, - StartingPosition = (uint)_preRacePlayerInfos.Count + 1, + Player = player, + PlayerLoaded = true, + PlayerIndex = (uint) _players.Count + 1, }); LoadPlayerCar(player); @@ -75,7 +74,7 @@ private void LoadPlayerCar(Player player) var startPosition = mainPath.Waypoints.First().Position + Vector3.UnitY * 3; var spacing = 15; - var range = _preRacePlayerInfos.Count * spacing; + var range = _players.Count * spacing; startPosition += Vector3.UnitZ * range; var startRotation = Quaternion.CreateFromYawPitchRoll(((float) Math.PI) * -0.5f, 0 , 0); @@ -144,23 +143,23 @@ private void LoadPlayerCar(Player player) }); // Register car for player - for (var i = 0; i < _preRacePlayerInfos.Count; i++) + for (var i = 0; i < _players.Count; i++) { - PreRacePlayerInfo info = _preRacePlayerInfos[i]; - if (info.PlayerId == player) + RacingPlayerInfo info = _players[i]; + if (info.Player == player) { - info.VehicleId = car; - _preRacePlayerInfos[i] = info; + info.Vehicle = car; + _players[i] = info; } } } private void InitRace() { - if (racingStatus == RacingStatus.Loaded) + if (_racingStatus == RacingStatus.Loaded) return; - racingStatus = RacingStatus.Loaded; + _racingStatus = RacingStatus.Loaded; Zone.BroadcastMessage(new NotifyRacingClientMessage { @@ -174,16 +173,16 @@ private void InitRace() private void StartRace() { - if (racingStatus == RacingStatus.Started) + if (_racingStatus == RacingStatus.Started) return; - racingStatus = RacingStatus.Started; + _racingStatus = RacingStatus.Started; - foreach (PreRacePlayerInfo info in _preRacePlayerInfos) + foreach (var info in _players) { Zone.BroadcastMessage(new VehicleUnlockInputMessage { - Associate = info.VehicleId, + Associate = info.Vehicle, LockWheels = false, }); } @@ -231,6 +230,18 @@ public void OnAcknowledgePossession(GameObject possessed) }); } + public void OnPlayerRequestDie(Player player) + { + + foreach (var racingPlayer in _players) + { + if (racingPlayer.Player != player) + continue; + + + } + } + public override void Construct(BitWriter writer) { this.Serialize(writer); @@ -257,11 +268,23 @@ public override void Serialize(BitWriter writer) packet.ExpectedPlayerCount = (ushort)this.Participants.Count; - packet.PreRacePlayerInfos = this._preRacePlayerInfos.ToArray(); + packet.PreRacePlayerInfos = this._players.Select(info => new PreRacePlayerInfo + { + Player = info.Player, + Vehicle = info.Vehicle, + StartingPosition = info.PlayerIndex, + IsReady = info.PlayerLoaded, + }).ToArray(); + + packet.RaceInfo = _raceInfo; - packet.RaceInfo = _raceInfo; // lap count displays correctly on the client + packet.DuringRacePlayerInfos = this._players.Select(info => new DuringRacePlayerInfo + { + Player = info.Player, + BestLapTime = (float) info.BestLapTime.TotalSeconds, + RaceTime = (float) info.RaceTime.TotalSeconds + }).ToArray(); - packet.DuringRacePlayerInfos = this._duringRacePlayerInfos.ToArray(); // TODO @@ -271,7 +294,27 @@ public override void Serialize(BitWriter writer) private enum RacingStatus { None, Loaded, - Started + Started, } + + struct RacingPlayerInfo { + public GameObject Player { get; set; } + public GameObject Vehicle { get; set; } + public uint PlayerIndex { get; set; } + public bool PlayerLoaded { get; set; } + public float[] Data { get; set; } + public Vector3 RespawnPosition { get; set; } + public Quaternion RespawnRotation { get; set; } + public uint RespawnIndex { get; set; } + public uint Lap { get; set; } + public uint Finished { get; set; } + public uint ReachedPoints { get; set; } + public TimeSpan BestLapTime { get; set; } + public TimeSpan LapTime { get; set; } + public uint SmashedTimes { get; set; } + public bool NoSmashOnReload { get; set; } + public bool CollectedRewards { get; set; } + public TimeSpan RaceTime { get; set; } + }; } } diff --git a/Uchu.World/Packets/Replica/Serialize/RacingControlSerialization.cs b/Uchu.World/Packets/Replica/Serialize/RacingControlSerialization.cs index 138cb147..2ccefe0b 100644 --- a/Uchu.World/Packets/Replica/Serialize/RacingControlSerialization.cs +++ b/Uchu.World/Packets/Replica/Serialize/RacingControlSerialization.cs @@ -4,21 +4,21 @@ namespace Uchu.World { [Struct] public struct PreRacePlayerInfo { - public GameObject PlayerId { get; set; } - public GameObject VehicleId { get; set; } + public GameObject Player { get; set; } + public GameObject Vehicle { get; set; } public uint StartingPosition { get; set; } public bool IsReady { get; set; } } [Struct] public struct PostRacePlayerInfo { - public GameObject PlayerId { get; set; } + public GameObject Player { get; set; } public uint CurrentRank { get; set; } } [Struct] public struct DuringRacePlayerInfo { - public GameObject PlayerId { get; set; } + public GameObject Player { get; set; } public float BestLapTime { get; set; } public float RaceTime { get; set; } } From 895eab35c20c96db36694a6ffc87cedc9561ddea Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Sat, 13 Nov 2021 23:09:47 +0100 Subject: [PATCH 14/71] Remove position update logging --- Uchu.World/Handlers/PositionUpdateHandler.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Uchu.World/Handlers/PositionUpdateHandler.cs b/Uchu.World/Handlers/PositionUpdateHandler.cs index e8c36b12..1f61027a 100644 --- a/Uchu.World/Handlers/PositionUpdateHandler.cs +++ b/Uchu.World/Handlers/PositionUpdateHandler.cs @@ -14,8 +14,6 @@ public async Task PositionHandler(PositionUpdatePacket packet, IRakConnection co var player = UchuServer.FindPlayer(connection); if (player?.Transform == default) return; - Logger.Debug(packet.Position); - // TODO: set position of possessed object? maybe? // The server is a slave to the position update packets it gets from the client right now. From ffa5e1dfcadc82eafe657eeeacfbfd8f289c14a4 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Sun, 14 Nov 2021 00:47:34 +0100 Subject: [PATCH 15/71] Imagination spawners --- .../Handlers/GameMessages/RacingHandler.cs | 7 ++ .../RacingControlComponent.cs | 64 +++++++++++++------ 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/Uchu.World/Handlers/GameMessages/RacingHandler.cs b/Uchu.World/Handlers/GameMessages/RacingHandler.cs index 2b212a71..18ec49fd 100644 --- a/Uchu.World/Handlers/GameMessages/RacingHandler.cs +++ b/Uchu.World/Handlers/GameMessages/RacingHandler.cs @@ -16,5 +16,12 @@ public void VehicleSetWheelLockStateHandler(VehicleSetWheelLockStateMessage mess Logger.Information($"Set wheel lock state: friction = {message.ExtraFriction}, locked = {message.Locked}"); // TODO: handle } + + [PacketHandler] + public async void ImaginationHandler(VehicleNotifyHitImaginationServerMessage message, Player player) + { + message.Associate.GetComponent().Imagination += 10; + await message.PickupObjId.GetComponent().SmashAsync(message.Associate); + } } } diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 4456ca8b..a0973bac 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Numerics; using System.Threading.Tasks; +using InfectedRose.Luz; using RakDotNet.IO; using Uchu.Core; @@ -22,6 +23,8 @@ public class RacingControlComponent : ScriptedActivityComponent private RacingStatus _racingStatus = RacingStatus.None; + private LuzPathData _path; + public RacingControlComponent() { _raceInfo.LapCount = 3; @@ -34,21 +37,23 @@ public RacingControlComponent() private async Task LoadAsync() { - Listen(this.GameObject.OnMessageBoxRespond, (player, message) => - { - Logger.Information($"Button - {message.Button} {message.Identifier} {message.UserData}"); - if (message.Identifier == "ACT_RACE_EXIT_THE_RACE?" && message.Button == 1) - { - // TODO: Player wants to leave race - } - }); + Listen(this.GameObject.OnMessageBoxRespond, OnMessageBoxRespond); Listen(Zone.OnPlayerLoad, player => { - Listen(player.OnWorldLoad, async () => OnPlayerLoad(player)); + Listen(player.OnWorldLoad, () => OnPlayerLoad(player)); }); } + private void OnMessageBoxRespond(Player player, MessageBoxRespondMessage message) + { + Logger.Information($"Button - {message.Button} {message.Identifier} {message.UserData}"); + if (message.Identifier == "ACT_RACE_EXIT_THE_RACE?" && message.Button == 1) + { + // TODO: Player wants to leave race + } + } + private void OnPlayerLoad(Player player) { Logger.Information("Player loaded into racing"); @@ -68,11 +73,11 @@ private void OnPlayerLoad(Player player) private void LoadPlayerCar(Player player) { // Get position and rotation - var mainPath = Zone.ZoneInfo.LuzFile.PathData.FirstOrDefault(path => path.PathName == "MainPath"); - if (mainPath == default) + _path = Zone.ZoneInfo.LuzFile.PathData.FirstOrDefault(path => path.PathName == "MainPath"); + if (_path == default) throw new Exception("Path not found"); - var startPosition = mainPath.Waypoints.First().Position + Vector3.UnitY * 3; + var startPosition = _path.Waypoints.First().Position + Vector3.UnitY * 3; var spacing = 15; var range = _players.Count * spacing; startPosition += Vector3.UnitZ * range; @@ -178,6 +183,34 @@ private void StartRace() _racingStatus = RacingStatus.Started; + // Start imagination spawners + var minSpawner = Zone.SpawnerNetworks.FirstOrDefault(gameObject => gameObject.Name == "ImaginationSpawn_Min"); + var medSpawner = Zone.SpawnerNetworks.FirstOrDefault(gameObject => gameObject.Name == "ImaginationSpawn_Med"); + var maxSpawner = Zone.SpawnerNetworks.FirstOrDefault(gameObject => gameObject.Name == "ImaginationSpawn_Max"); + + minSpawner?.Activate(); + minSpawner?.SpawnAll(); + + if (_players.Count > 2) + { + medSpawner?.Activate(); + medSpawner?.SpawnAll(); + } + + if (_players.Count > 4) + { + maxSpawner?.Activate(); + maxSpawner?.SpawnAll(); + } + + // Respawn points + foreach (var luzPathWaypoint in _path.Waypoints) + { + // luzPathWaypoint.Position; + + } + + // Go! foreach (var info in _players) { Zone.BroadcastMessage(new VehicleUnlockInputMessage @@ -232,14 +265,9 @@ public void OnAcknowledgePossession(GameObject possessed) public void OnPlayerRequestDie(Player player) { + var racingPlayer = _players.Find(info => info.Player == player); - foreach (var racingPlayer in _players) - { - if (racingPlayer.Player != player) - continue; - - } } public override void Construct(BitWriter writer) From 4d013e72f3d381ed3f0fc51be59bb874bb8c71c9 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Sun, 14 Nov 2021 11:05:35 +0100 Subject: [PATCH 16/71] Implement exit button for racing --- .../Components/ReplicaComponents/RacingControlComponent.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index a0973bac..7219b7bf 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -50,7 +50,8 @@ private void OnMessageBoxRespond(Player player, MessageBoxRespondMessage message Logger.Information($"Button - {message.Button} {message.Identifier} {message.UserData}"); if (message.Identifier == "ACT_RACE_EXIT_THE_RACE?" && message.Button == 1) { - // TODO: Player wants to leave race + _players.RemoveAll(info => info.Player == player); + player.SendToWorldAsync(1200, new Vector3(248.8f, 287.4f, 186.9f), new Quaternion(0, 0.7f, 0, 0.7f)); } } From f58f9143b0dd6fe62c3838e7ae3833c12817a89c Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Sun, 14 Nov 2021 11:42:33 +0100 Subject: [PATCH 17/71] Fix ModuleAssembly serialization (fix imagination) --- .../ReplicaComponents/ModuleAssemblyComponent.cs | 10 ++++------ .../Replica/Serialize/ModuleAssemblySerialization.cs | 6 +++++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/ModuleAssemblyComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/ModuleAssemblyComponent.cs index dccad28e..3b70024e 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/ModuleAssemblyComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/ModuleAssemblyComponent.cs @@ -1,7 +1,7 @@ namespace Uchu.World { - public class ModuleAssemblyComponent : StructReplicaComponent + public class ModuleAssemblyComponent : StructReplicaComponent { public override ComponentId Id => ComponentId.ModuleAssemblyComponent; @@ -17,12 +17,12 @@ public string GetAssembly() return this._parts; } - public override ModuleAssemblySerialization GetConstructPacket() + public override ModuleAssemblyConstruction GetConstructPacket() { var packet = base.GetConstructPacket(); packet.ModuleAssemblyInfo = new ModuleAssemblyInfo { - Assembly = GameObject.InvalidObject, // this.GameObject ? subkey ? + Assembly = GameObject.InvalidObject, // subkey Blob = _parts, UseOptionalParts = false, }; @@ -32,10 +32,8 @@ public override ModuleAssemblySerialization GetConstructPacket() public override ModuleAssemblySerialization GetSerializePacket() { - var packet = this.GetConstructPacket(); - packet.Flag = false; + var packet = base.GetSerializePacket(); return packet; } - } } diff --git a/Uchu.World/Packets/Replica/Serialize/ModuleAssemblySerialization.cs b/Uchu.World/Packets/Replica/Serialize/ModuleAssemblySerialization.cs index 05087db6..f3e0c4e0 100644 --- a/Uchu.World/Packets/Replica/Serialize/ModuleAssemblySerialization.cs +++ b/Uchu.World/Packets/Replica/Serialize/ModuleAssemblySerialization.cs @@ -2,13 +2,17 @@ namespace Uchu.World { - public struct ModuleAssemblySerialization + public struct ModuleAssemblyConstruction { public bool Flag { get; set; } [Requires("Flag")] public ModuleAssemblyInfo ModuleAssemblyInfo { get; set; } } + public struct ModuleAssemblySerialization + { + } + [Struct] public struct ModuleAssemblyInfo { From d60cdc19928488639a3767046320700f31c71ca2 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Sun, 14 Nov 2021 22:48:49 +0100 Subject: [PATCH 18/71] Position updates --- Uchu.World/Handlers/PositionUpdateHandler.cs | 15 ++++++++++++++- .../ReplicaComponents/VehiclePhysicsComponent.cs | 12 ++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Uchu.World/Handlers/PositionUpdateHandler.cs b/Uchu.World/Handlers/PositionUpdateHandler.cs index 1f61027a..d3168e58 100644 --- a/Uchu.World/Handlers/PositionUpdateHandler.cs +++ b/Uchu.World/Handlers/PositionUpdateHandler.cs @@ -14,7 +14,20 @@ public async Task PositionHandler(PositionUpdatePacket packet, IRakConnection co var player = UchuServer.FindPlayer(connection); if (player?.Transform == default) return; - // TODO: set position of possessed object? maybe? + // Update position of possessed object & serialize it + var vehicle = player.GetComponent().VehicleObject; + if (vehicle != null) + { + vehicle.Transform.Position = packet.Position; + vehicle.Transform.Rotation = packet.Rotation; + if (vehicle.TryGetComponent(out var vehiclePhysics)) + { + vehiclePhysics.LinearVelocity = packet.Velocity; + vehiclePhysics.AngularVelocity = packet.AngularVelocity; + } + GameObject.Serialize(vehicle); + } + // The server is a slave to the position update packets it gets from the client right now. player.Transform.Position = packet.Position; diff --git a/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs index 65396644..e4fb3871 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs @@ -8,6 +8,10 @@ public class VehiclePhysicsComponent : StructReplicaComponent ComponentId.VehiclePhysicsComponent; + public Vector3 AngularVelocity { get; set; } = Vector3.Zero; + + public Vector3 LinearVelocity { get; set; } = Vector3.Zero; + public override VehiclePhysicsConstruction GetConstructPacket() { var packet = base.GetConstructPacket(); @@ -15,8 +19,8 @@ public override VehiclePhysicsConstruction GetConstructPacket() { Position = this.GameObject.Transform.Position, Rotation = this.GameObject.Transform.Rotation, - AngularVelocity = Vector3.Zero, - LinearVelocity = Vector3.Zero, + AngularVelocity = this.AngularVelocity, + LinearVelocity = this.LinearVelocity, IsOnGround = true, IsOnRail = false, RemoteInputPing = 0.0f, @@ -36,8 +40,8 @@ public override VehiclePhysicsSerialization GetSerializePacket() { Position = this.GameObject.Transform.Position, Rotation = this.GameObject.Transform.Rotation, - AngularVelocity = Vector3.Zero, - LinearVelocity = Vector3.Zero, + AngularVelocity = this.AngularVelocity, + LinearVelocity = this.LinearVelocity, IsOnGround = true, IsOnRail = false, RemoteInputPing = 0.0f, From d778fd22c2d42b3985cdb82c313523b3a6b089a9 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Mon, 15 Nov 2021 12:08:51 +0100 Subject: [PATCH 19/71] Respawning --- .../Handlers/GameMessages/GeneralHandler.cs | 6 + .../Handlers/GameMessages/RacingHandler.cs | 6 + .../RacingControlComponent.cs | 143 +++++++++++++++--- Uchu.World/Objects/GameObjects/GameObject.cs | 8 + 4 files changed, 145 insertions(+), 18 deletions(-) diff --git a/Uchu.World/Handlers/GameMessages/GeneralHandler.cs b/Uchu.World/Handlers/GameMessages/GeneralHandler.cs index 209f64a0..005213d6 100644 --- a/Uchu.World/Handlers/GameMessages/GeneralHandler.cs +++ b/Uchu.World/Handlers/GameMessages/GeneralHandler.cs @@ -81,6 +81,12 @@ public async Task RequestSmashHandler(RequestSmashPlayerMessage message, Player await player.GetComponent().SmashAsync(player, player); } + [PacketHandler] + public async Task RequestDieHandler(RequestDieMessage message, Player player) + { + await player.OnRequestDie.InvokeAsync(message); + } + [PacketHandler] public void RebuildCancelHandler(RebuildCancelMessage message, Player player) { diff --git a/Uchu.World/Handlers/GameMessages/RacingHandler.cs b/Uchu.World/Handlers/GameMessages/RacingHandler.cs index 18ec49fd..644b1be0 100644 --- a/Uchu.World/Handlers/GameMessages/RacingHandler.cs +++ b/Uchu.World/Handlers/GameMessages/RacingHandler.cs @@ -23,5 +23,11 @@ public async void ImaginationHandler(VehicleNotifyHitImaginationServerMessage me message.Associate.GetComponent().Imagination += 10; await message.PickupObjId.GetComponent().SmashAsync(message.Associate); } + + [PacketHandler] + public async void RacingPlayerInfoResetFinishedHandler(RacingPlayerInfoResetFinishedMessage message, Player player) + { + await player.OnRacingPlayerInfoResetFinished.InvokeAsync(); + } } } diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 7219b7bf..9ff45eab 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -10,6 +10,7 @@ using InfectedRose.Luz; using RakDotNet.IO; using Uchu.Core; +using Uchu.Physics; namespace Uchu.World { @@ -37,24 +38,42 @@ public RacingControlComponent() private async Task LoadAsync() { + _path = Zone.ZoneInfo.LuzFile.PathData.FirstOrDefault(path => path.PathName == "MainPath"); + + if (_path == default) + throw new Exception("Path not found"); + Listen(this.GameObject.OnMessageBoxRespond, OnMessageBoxRespond); Listen(Zone.OnPlayerLoad, player => { Listen(player.OnWorldLoad, () => OnPlayerLoad(player)); + Listen(player.OnRequestDie, (msg) => OnPlayerRequestDie(player)); + Listen(player.OnRacingPlayerInfoResetFinished, () => OnRacingPlayerInfoResetFinished(player)); + Listen(player.OnAcknowledgePossession, vehicle => OnAcknowledgePossession(player, vehicle)); }); } + /// + /// Message box response handler + /// + /// + /// private void OnMessageBoxRespond(Player player, MessageBoxRespondMessage message) { Logger.Information($"Button - {message.Button} {message.Identifier} {message.UserData}"); if (message.Identifier == "ACT_RACE_EXIT_THE_RACE?" && message.Button == 1) { _players.RemoveAll(info => info.Player == player); + // TODO: use zone corresponding to this racing world player.SendToWorldAsync(1200, new Vector3(248.8f, 287.4f, 186.9f), new Quaternion(0, 0.7f, 0, 0.7f)); } } + /// + /// This runs when the player loads into the world, it registers the player + /// + /// private void OnPlayerLoad(Player player) { Logger.Information("Player loaded into racing"); @@ -65,19 +84,21 @@ private void OnPlayerLoad(Player player) Player = player, PlayerLoaded = true, PlayerIndex = (uint) _players.Count + 1, + NoSmashOnReload = true, }); LoadPlayerCar(player); Zone.Schedule(InitRace, 10000); } + /// + /// Set up the player's car + /// + /// + /// private void LoadPlayerCar(Player player) { // Get position and rotation - _path = Zone.ZoneInfo.LuzFile.PathData.FirstOrDefault(path => path.PathName == "MainPath"); - if (_path == default) - throw new Exception("Path not found"); - var startPosition = _path.Waypoints.First().Position + Vector3.UnitY * 3; var spacing = 15; var range = _players.Count * spacing; @@ -88,7 +109,7 @@ private void LoadPlayerCar(Player player) // Create car player.Teleport(startPosition, startRotation); GameObject car = GameObject.Instantiate(this.GameObject.Zone.ZoneControlObject, 8092, startPosition, startRotation); - + // Setup imagination var destroyableComponent = car.GetComponent(); destroyableComponent.MaxImagination = 60; @@ -205,10 +226,36 @@ private void StartRace() } // Respawn points - foreach (var luzPathWaypoint in _path.Waypoints) + // This is not the most efficient way to do this. + // This checks the distance to every respawn point every physics tick, + // but we only really need to check the next one (or nearest few). + for (var i = 0; i < _path.Waypoints.Length; i++) { - // luzPathWaypoint.Position; + var proximityObject = GameObject.Instantiate(GameObject.Zone); + var physics = proximityObject.AddComponent(); + var physicsObject = SphereBody.Create( + GameObject.Zone.Simulation, + _path.Waypoints[i].Position, + 50f); + physics.SetPhysics(physicsObject); + + // Listen for players entering and leaving. + var playersInPhysicsObject = new List(); + var index = i; + this.Listen(physics.OnEnter, component => + { + if (!(component.GameObject is Player player)) return; + if (playersInPhysicsObject.Contains(player)) return; + playersInPhysicsObject.Add(player); + this.PlayerReachedCheckpoint(player, index); + }); + this.Listen(physics.OnLeave, component => + { + if (!(component.GameObject is Player player)) return; + if (!playersInPhysicsObject.Contains(player)) return; + playersInPhysicsObject.Remove(player); + }); } // Go! @@ -220,7 +267,7 @@ private void StartRace() LockWheels = false, }); } - + Zone.BroadcastMessage(new ActivityStartMessage { Associate = this.GameObject, @@ -248,27 +295,87 @@ private void StartRace() // y = 0.8638404011726379 // z = 0.0 - public void OnAcknowledgePossession(GameObject possessed) + public void OnAcknowledgePossession(Player player, GameObject vehicle) { - GameObject.Serialize(this.GameObject); + } - Zone.BroadcastMessage(new NotifyRacingClientMessage - { - Associate = this.GameObject, - EventType = RacingClientNotificationType.ActivityStart, - }); + /// + /// Respawns the player's car after a crash + /// + /// + private void OnRacingPlayerInfoResetFinished(Player player) + { + var playerInfoIndex = _players.FindIndex(x => x.Player == player); + var playerInfo = _players[playerInfoIndex]; + var car = playerInfo.Vehicle; - Zone.BroadcastMessage(new ActivityStartMessage + if (!playerInfo.NoSmashOnReload) { - Associate = this.GameObject, - }); + car.GetComponent().SmashAsync(player); + + player.Message(new VehicleUnlockInputMessage + { + Associate = car, + LockWheels = false, + }); + + player.Message(new VehicleSetWheelLockStateMessage + { + Associate = car, + ExtraFriction = false, + Locked = false, + }); + + car.GetComponent().ResurrectAsync(); + + Zone.BroadcastMessage(new ResurrectMessage + { + Associate = car, + }); + } + + playerInfo.NoSmashOnReload = false; + _players[playerInfoIndex] = playerInfo; } + /// + /// Handler for when the client tells the server that the car crashed + /// + /// public void OnPlayerRequestDie(Player player) { var racingPlayer = _players.Find(info => info.Player == player); + Logger.Information($"{player} requested death - respawning to {racingPlayer.RespawnPosition}"); + + player.Message(new RacingSetPlayerResetInfoMessage + { + Associate = GameObject, + CurrentLap = (int) racingPlayer.Lap, + FurthestResetPlane = racingPlayer.RespawnIndex, + PlayerId = player, + RespawnPos = racingPlayer.RespawnPosition, + UpcomingPlane = racingPlayer.RespawnIndex + 1, + }); + + player.Message(new RacingResetPlayerToLastResetMessage + { + Associate = GameObject, + PlayerId = player, + }); + } + private void PlayerReachedCheckpoint(Player player, int index) + { + var waypoint = _path.Waypoints[index]; + var playerInfoIndex = _players.FindIndex(x => x.Player == player); + var playerInfo = _players[playerInfoIndex]; + playerInfo.RespawnIndex = (uint) index; + playerInfo.RespawnPosition = waypoint.Position; + playerInfo.RespawnRotation = player.Transform.Rotation; + _players[playerInfoIndex] = playerInfo; + + Logger.Information($"Player reached checkpoint: {index}"); } public override void Construct(BitWriter writer) diff --git a/Uchu.World/Objects/GameObjects/GameObject.cs b/Uchu.World/Objects/GameObjects/GameObject.cs index 43c72487..88acce16 100644 --- a/Uchu.World/Objects/GameObjects/GameObject.cs +++ b/Uchu.World/Objects/GameObjects/GameObject.cs @@ -112,6 +112,10 @@ public int GameMasterLevel public Event OnChoiceBoxRespond { get; } + public Event OnRequestDie { get; } + + public Event OnRacingPlayerInfoResetFinished { get; } + #endregion #region Macro @@ -150,6 +154,10 @@ protected GameObject() OnChoiceBoxRespond = new Event(); + OnRequestDie = new Event(); + + OnRacingPlayerInfoResetFinished = new Event(); + Listen(OnStart, () => { foreach (var component in Components.ToArray()) Start(component); From f65083b80cc5f58cd768293a66a58c9de5673315 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Mon, 15 Nov 2021 19:59:42 +0100 Subject: [PATCH 20/71] Implement CarBoostBehavior --- .../Server/VehicleAddPassiveBoostAction.cs | 9 +++++ .../Server/VehicleRemovePassiveBoostAction.cs | 9 +++++ .../Systems/Behaviors/CarBoostBehavior.cs | 37 ++++++++++++++++++- 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 Uchu.World/Packets/GameMessages/Server/VehicleAddPassiveBoostAction.cs create mode 100644 Uchu.World/Packets/GameMessages/Server/VehicleRemovePassiveBoostAction.cs diff --git a/Uchu.World/Packets/GameMessages/Server/VehicleAddPassiveBoostAction.cs b/Uchu.World/Packets/GameMessages/Server/VehicleAddPassiveBoostAction.cs new file mode 100644 index 00000000..e4185342 --- /dev/null +++ b/Uchu.World/Packets/GameMessages/Server/VehicleAddPassiveBoostAction.cs @@ -0,0 +1,9 @@ +namespace Uchu.World +{ + [ServerGameMessagePacketStruct] + public struct VehicleAddPassiveBoostAction + { + public GameObject Associate { get; set; } + public GameMessageId GameMessageId => GameMessageId.VehicleAddPassiveBoostAction; + } +} \ No newline at end of file diff --git a/Uchu.World/Packets/GameMessages/Server/VehicleRemovePassiveBoostAction.cs b/Uchu.World/Packets/GameMessages/Server/VehicleRemovePassiveBoostAction.cs new file mode 100644 index 00000000..4f628974 --- /dev/null +++ b/Uchu.World/Packets/GameMessages/Server/VehicleRemovePassiveBoostAction.cs @@ -0,0 +1,9 @@ +namespace Uchu.World +{ + [ServerGameMessagePacketStruct] + public struct VehicleRemovePassiveBoostAction + { + public GameObject Associate { get; set; } + public GameMessageId GameMessageId => GameMessageId.VehicleRemovePassiveBoostAction; + } +} \ No newline at end of file diff --git a/Uchu.World/Systems/Behaviors/CarBoostBehavior.cs b/Uchu.World/Systems/Behaviors/CarBoostBehavior.cs index 77f387fb..4899fee1 100644 --- a/Uchu.World/Systems/Behaviors/CarBoostBehavior.cs +++ b/Uchu.World/Systems/Behaviors/CarBoostBehavior.cs @@ -1,8 +1,43 @@ +using System.Threading.Tasks; + namespace Uchu.World.Systems.Behaviors { public class CarBoostBehavior : BehaviorBase { public override BehaviorTemplateId Id => BehaviorTemplateId.CarBoost; - // TODO: figure out what to do here + + // Action to execute on success + private BehaviorBase Action; + // Action to execute when boost failed + private BehaviorBase ActionFailed; + // Time of boost + private float Time; + + public override async Task BuildAsync() + { + Action = await GetBehavior(await GetParameter("action")); + Time = await GetParameter("time"); + } + + public override void ExecuteStart(BehaviorExecutionParameters parameters) + { + if (!(parameters.Context.Associate is Player player)) + return; + + Action.ExecuteStart(parameters); + + player.Zone.BroadcastMessage(new VehicleAddPassiveBoostAction + { + Associate = player + }); + + player.Zone.Schedule(() => + { + player.Zone.BroadcastMessage(new VehicleRemovePassiveBoostAction + { + Associate = player + }); + }, Time * 1000); + } } } From 48b1935f0e84e48b854f041fc34142401d8e856a Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Mon, 15 Nov 2021 20:10:58 +0100 Subject: [PATCH 21/71] Fix FindItem for first item in inventory --- .../Objects/Components/Player/InventoryManagerComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Uchu.World/Objects/Components/Player/InventoryManagerComponent.cs b/Uchu.World/Objects/Components/Player/InventoryManagerComponent.cs index 19df0f46..93c0cb5f 100644 --- a/Uchu.World/Objects/Components/Player/InventoryManagerComponent.cs +++ b/Uchu.World/Objects/Components/Player/InventoryManagerComponent.cs @@ -193,7 +193,7 @@ public Item FindItem(Lot lot) { return _inventories.Values.Select( inventory => inventory.Items.FirstOrDefault(i => i.Lot == lot) - ).FirstOrDefault(item => item != default); + ).FirstOrDefault(item => item != null); } /// From 3cc1f280674cd708059d08de05a96b93523f61b1 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Mon, 15 Nov 2021 20:33:58 +0100 Subject: [PATCH 22/71] Ignore vehicle object in /near --- Uchu.World/Handlers/Commands/CharacterCommandHandler.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Uchu.World/Handlers/Commands/CharacterCommandHandler.cs b/Uchu.World/Handlers/Commands/CharacterCommandHandler.cs index 1f90f59c..06ea56b4 100644 --- a/Uchu.World/Handlers/Commands/CharacterCommandHandler.cs +++ b/Uchu.World/Handlers/Commands/CharacterCommandHandler.cs @@ -354,6 +354,9 @@ public string Fx(string[] arguments, Player player) [CommandHandler(Signature = "near", Help = "Get nearest object", GameMasterLevel = GameMasterLevel.Player)] public string Near(string[] arguments, Player player) { + // Ignore the player's car (if relevant) + var vehicle = player.GetComponent().VehicleObject; + var current = player.Zone.GameObjects[0]; if (!arguments.Contains("-m")) @@ -362,6 +365,8 @@ public string Near(string[] arguments, Player player) { if (gameObject.Transform == default) continue; + if (gameObject == vehicle) continue; + if (!arguments.Contains("-sp")) { if (gameObject.TryGetComponent(out _)) continue; From 036ef5963b61939e6a707915ca2477eee4b47eb8 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Mon, 15 Nov 2021 20:34:58 +0100 Subject: [PATCH 23/71] Reset car if it has fallen off the map --- .../RacingControlComponent.cs | 69 +++++++++++-------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 9ff45eab..803c8f4a 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -26,6 +26,8 @@ public class RacingControlComponent : ScriptedActivityComponent private LuzPathData _path; + private int _deathPlaneHeight; + public RacingControlComponent() { _raceInfo.LapCount = 3; @@ -40,6 +42,22 @@ private async Task LoadAsync() { _path = Zone.ZoneInfo.LuzFile.PathData.FirstOrDefault(path => path.PathName == "MainPath"); + switch (Zone.ZoneId) + { + case 1203: + _deathPlaneHeight = 100; + break; + case 1303: + _deathPlaneHeight = 0; + break; + case 1403: + _deathPlaneHeight = 300; + break; + default: + _deathPlaneHeight = 0; + break; + } + if (_path == default) throw new Exception("Path not found"); @@ -88,6 +106,15 @@ private void OnPlayerLoad(Player player) }); LoadPlayerCar(player); + + Listen(player.OnPositionUpdate, (position, rotation) => + { + if (position.Y < _deathPlaneHeight) + { + OnPlayerRequestDie(player); + } + }); + Zone.Schedule(InitRace, 10000); } @@ -183,7 +210,7 @@ private void LoadPlayerCar(Player player) private void InitRace() { - if (_racingStatus == RacingStatus.Loaded) + if (_racingStatus != RacingStatus.None) return; _racingStatus = RacingStatus.Loaded; @@ -200,7 +227,7 @@ private void InitRace() private void StartRace() { - if (_racingStatus == RacingStatus.Started) + if (_racingStatus != RacingStatus.Loaded) return; _racingStatus = RacingStatus.Started; @@ -276,25 +303,6 @@ private void StartRace() GameObject.Serialize(this.GameObject); } - // todo - // RacingClientReady client->server - // RacingPlayerLoaded server->client - // RacingResetPlayerToLastReset server->client --> this is like player reached respawn checkpoint - // RacingSetPlayerResetInfo server->client - // RacingPlayerInfoResetFinished client->server - - // in capture even before acknowledge poss: - // teleport msg [player] - // bIgnoreY = False - // bSetRotation = True - // bSkipAllChecks = False - // pos = (-1457.711669921875, 794.0, -351.61419677734375) - // useNavmesh = False - // w = 0.5037656426429749 - // x = 0.0 - // y = 0.8638404011726379 - // z = 0.0 - public void OnAcknowledgePossession(Player player, GameObject vehicle) { } @@ -311,7 +319,13 @@ private void OnRacingPlayerInfoResetFinished(Player player) if (!playerInfo.NoSmashOnReload) { - car.GetComponent().SmashAsync(player); + Zone.BroadcastMessage(new DieMessage + { + Associate = car, + DeathType = "violent", + Killer = player, + SpawnLoot = false, + }); player.Message(new VehicleUnlockInputMessage { @@ -326,8 +340,6 @@ private void OnRacingPlayerInfoResetFinished(Player player) Locked = false, }); - car.GetComponent().ResurrectAsync(); - Zone.BroadcastMessage(new ResurrectMessage { Associate = car, @@ -348,17 +360,20 @@ public void OnPlayerRequestDie(Player player) Logger.Information($"{player} requested death - respawning to {racingPlayer.RespawnPosition}"); - player.Message(new RacingSetPlayerResetInfoMessage + if (racingPlayer.RespawnPosition == Vector3.Zero) + racingPlayer.RespawnPosition = _path.Waypoints.First().Position; + + Zone.BroadcastMessage(new RacingSetPlayerResetInfoMessage { Associate = GameObject, CurrentLap = (int) racingPlayer.Lap, FurthestResetPlane = racingPlayer.RespawnIndex, PlayerId = player, - RespawnPos = racingPlayer.RespawnPosition, + RespawnPos = racingPlayer.RespawnPosition + Vector3.UnitY * 5, UpcomingPlane = racingPlayer.RespawnIndex + 1, }); - player.Message(new RacingResetPlayerToLastResetMessage + Zone.BroadcastMessage(new RacingResetPlayerToLastResetMessage { Associate = GameObject, PlayerId = player, From c8203dc996a01130a22d3b5a5e97aedcd4b57182 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Mon, 15 Nov 2021 21:13:06 +0100 Subject: [PATCH 24/71] Add zone return data for racing --- .../RacingControlComponent.cs | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 803c8f4a..54d0651e 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -28,6 +28,8 @@ public class RacingControlComponent : ScriptedActivityComponent private int _deathPlaneHeight; + private MainWorldReturnData _returnData; + public RacingControlComponent() { _raceInfo.LapCount = 3; @@ -46,15 +48,39 @@ private async Task LoadAsync() { case 1203: _deathPlaneHeight = 100; + _returnData = new MainWorldReturnData + { + ZoneId = 1200, + Position = new Vector3(248.8f, 287.4f, 186.9f), + Rotation = new Quaternion(0, 0.7f, 0, 0.7f), + }; break; case 1303: _deathPlaneHeight = 0; + _returnData = new MainWorldReturnData + { + ZoneId = 1300, + Position = new Vector3(63.5f, 314.8f, 493.1f), + Rotation = new Quaternion(0, 0.45f, 0, 0.89f), + }; break; case 1403: _deathPlaneHeight = 300; + _returnData = new MainWorldReturnData + { + ZoneId = 1400, + Position = new Vector3(775.4f, 238.4f, 455.2f), + Rotation = new Quaternion(0, 0.59f, 0, 0.8f), + }; break; default: _deathPlaneHeight = 0; + _returnData = new MainWorldReturnData + { + ZoneId = 1200, + Position = new Vector3(248.8f, 287.4f, 186.9f), + Rotation = new Quaternion(0, 0.7f, 0, 0.7f), + }; break; } @@ -82,9 +108,7 @@ private void OnMessageBoxRespond(Player player, MessageBoxRespondMessage message Logger.Information($"Button - {message.Button} {message.Identifier} {message.UserData}"); if (message.Identifier == "ACT_RACE_EXIT_THE_RACE?" && message.Button == 1) { - _players.RemoveAll(info => info.Player == player); - // TODO: use zone corresponding to this racing world - player.SendToWorldAsync(1200, new Vector3(248.8f, 287.4f, 186.9f), new Quaternion(0, 0.7f, 0, 0.7f)); + SendPlayerToMainWorld(player); } } @@ -96,6 +120,13 @@ private void OnPlayerLoad(Player player) { Logger.Information("Player loaded into racing"); + // If race has already started return player to main world + if (_racingStatus != RacingStatus.None) + { + SendPlayerToMainWorld(player); + return; + } + // Register player this._players.Add(new RacingPlayerInfo { @@ -118,6 +149,16 @@ private void OnPlayerLoad(Player player) Zone.Schedule(InitRace, 10000); } + /// + /// Send the player back to the world he came from + /// + /// + private void SendPlayerToMainWorld(Player player) + { + _players.RemoveAll(info => info.Player == player); + player.SendToWorldAsync(_returnData.ZoneId, _returnData.Position, _returnData.Rotation); + } + /// /// Set up the player's car /// @@ -448,6 +489,12 @@ private enum RacingStatus { Started, } + private struct MainWorldReturnData { + public ZoneId ZoneId { get; set; } + public Vector3 Position { get; set; } + public Quaternion Rotation { get; set; } + } + struct RacingPlayerInfo { public GameObject Player { get; set; } public GameObject Vehicle { get; set; } From a4e30825d92171545c2e98abc59d1a357054933e Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Tue, 16 Nov 2021 13:28:19 +0100 Subject: [PATCH 25/71] Communicate car boost to others --- Uchu.World/Systems/Behaviors/CarBoostBehavior.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Uchu.World/Systems/Behaviors/CarBoostBehavior.cs b/Uchu.World/Systems/Behaviors/CarBoostBehavior.cs index 4899fee1..6e8351a4 100644 --- a/Uchu.World/Systems/Behaviors/CarBoostBehavior.cs +++ b/Uchu.World/Systems/Behaviors/CarBoostBehavior.cs @@ -28,14 +28,14 @@ public override void ExecuteStart(BehaviorExecutionParameters parameters) player.Zone.BroadcastMessage(new VehicleAddPassiveBoostAction { - Associate = player + Associate = parameters.BranchContext.Target, }); player.Zone.Schedule(() => { player.Zone.BroadcastMessage(new VehicleRemovePassiveBoostAction { - Associate = player + Associate = parameters.BranchContext.Target, }); }, Time * 1000); } From 93a2c3fed51250dcb55fa95ea9cf343d865b98b7 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Tue, 16 Nov 2021 15:06:52 +0100 Subject: [PATCH 26/71] Fix position and roatation before race --- .../ReplicaComponents/RacingControlComponent.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 54d0651e..25a30775 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -140,7 +140,7 @@ private void OnPlayerLoad(Player player) Listen(player.OnPositionUpdate, (position, rotation) => { - if (position.Y < _deathPlaneHeight) + if (position.Y < _deathPlaneHeight && _racingStatus == RacingStatus.Started) { OnPlayerRequestDie(player); } @@ -167,12 +167,13 @@ private void SendPlayerToMainWorld(Player player) private void LoadPlayerCar(Player player) { // Get position and rotation - var startPosition = _path.Waypoints.First().Position + Vector3.UnitY * 3; + var waypoint = (LuzRaceWaypoint)_path.Waypoints.First(); + var startPosition = waypoint.Position; + var startRotation = waypoint.Rotation; + var spacing = 15; - var range = _players.Count * spacing; - startPosition += Vector3.UnitZ * range; - - var startRotation = Quaternion.CreateFromYawPitchRoll(((float) Math.PI) * -0.5f, 0 , 0); + var positionOffset = startRotation.VectorMultiply(Vector3.UnitX) * _players.Count * spacing; + startPosition += positionOffset + Vector3.UnitY * 3; // Create car player.Teleport(startPosition, startRotation); From 4aa519a2286de42c958beb66c0ee6db927c63394 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Wed, 17 Nov 2021 20:32:24 +0100 Subject: [PATCH 27/71] More boost communication --- .../Handlers/GameMessages/RacingHandler.cs | 18 ++++++++++++++++++ .../RacingControlComponent.cs | 5 +++++ ...NotifyServerAddPassiveBoostActionMessage.cs | 8 ++++++++ ...ifyServerRemovePassiveBoostActionMessage.cs | 8 ++++++++ .../Systems/Behaviors/CarBoostBehavior.cs | 8 ++++---- 5 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 Uchu.World/Packets/GameMessages/Client/VehicleNotifyServerAddPassiveBoostActionMessage.cs create mode 100644 Uchu.World/Packets/GameMessages/Client/VehicleNotifyServerRemovePassiveBoostActionMessage.cs diff --git a/Uchu.World/Handlers/GameMessages/RacingHandler.cs b/Uchu.World/Handlers/GameMessages/RacingHandler.cs index 644b1be0..3e3ce8b3 100644 --- a/Uchu.World/Handlers/GameMessages/RacingHandler.cs +++ b/Uchu.World/Handlers/GameMessages/RacingHandler.cs @@ -29,5 +29,23 @@ public async void RacingPlayerInfoResetFinishedHandler(RacingPlayerInfoResetFini { await player.OnRacingPlayerInfoResetFinished.InvokeAsync(); } + + [PacketHandler] + public void VehicleNotifyServerAddPassiveBoostActionHandler(VehicleNotifyServerAddPassiveBoostActionMessage message, Player player) + { + player.Zone.ExcludingMessage(new VehicleAddPassiveBoostAction + { + Associate = message.Associate, + }, player); + } + + [PacketHandler] + public void VehicleNotifyServerRemovePassiveBoostActionHandler(VehicleNotifyServerRemovePassiveBoostActionMessage message, Player player) + { + player.Zone.ExcludingMessage(new VehicleRemovePassiveBoostAction + { + Associate = message.Associate, + }, player); + } } } diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 25a30775..ee5c3ded 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -405,6 +405,11 @@ public void OnPlayerRequestDie(Player player) if (racingPlayer.RespawnPosition == Vector3.Zero) racingPlayer.RespawnPosition = _path.Waypoints.First().Position; + player.Zone.ExcludingMessage(new VehicleRemovePassiveBoostAction + { + Associate = racingPlayer.Vehicle, + }, player); + Zone.BroadcastMessage(new RacingSetPlayerResetInfoMessage { Associate = GameObject, diff --git a/Uchu.World/Packets/GameMessages/Client/VehicleNotifyServerAddPassiveBoostActionMessage.cs b/Uchu.World/Packets/GameMessages/Client/VehicleNotifyServerAddPassiveBoostActionMessage.cs new file mode 100644 index 00000000..83da5f21 --- /dev/null +++ b/Uchu.World/Packets/GameMessages/Client/VehicleNotifyServerAddPassiveBoostActionMessage.cs @@ -0,0 +1,8 @@ +namespace Uchu.World; + +[ClientGameMessagePacketStruct] +public struct VehicleNotifyServerAddPassiveBoostActionMessage +{ + public GameObject Associate { get; set; } + public GameMessageId GameMessageId => GameMessageId.VehicleNotifyServerAddPassiveBoostAction; +} diff --git a/Uchu.World/Packets/GameMessages/Client/VehicleNotifyServerRemovePassiveBoostActionMessage.cs b/Uchu.World/Packets/GameMessages/Client/VehicleNotifyServerRemovePassiveBoostActionMessage.cs new file mode 100644 index 00000000..8d909e54 --- /dev/null +++ b/Uchu.World/Packets/GameMessages/Client/VehicleNotifyServerRemovePassiveBoostActionMessage.cs @@ -0,0 +1,8 @@ +namespace Uchu.World; + +[ClientGameMessagePacketStruct] +public struct VehicleNotifyServerRemovePassiveBoostActionMessage +{ + public GameObject Associate { get; set; } + public GameMessageId GameMessageId => GameMessageId.VehicleNotifyServerRemovePassiveBoostAction; +} diff --git a/Uchu.World/Systems/Behaviors/CarBoostBehavior.cs b/Uchu.World/Systems/Behaviors/CarBoostBehavior.cs index 6e8351a4..491f162a 100644 --- a/Uchu.World/Systems/Behaviors/CarBoostBehavior.cs +++ b/Uchu.World/Systems/Behaviors/CarBoostBehavior.cs @@ -26,17 +26,17 @@ public override void ExecuteStart(BehaviorExecutionParameters parameters) Action.ExecuteStart(parameters); - player.Zone.BroadcastMessage(new VehicleAddPassiveBoostAction + player.Zone.ExcludingMessage(new VehicleAddPassiveBoostAction { Associate = parameters.BranchContext.Target, - }); + }, player); player.Zone.Schedule(() => { - player.Zone.BroadcastMessage(new VehicleRemovePassiveBoostAction + player.Zone.ExcludingMessage(new VehicleRemovePassiveBoostAction { Associate = parameters.BranchContext.Target, - }); + }, player); }, Time * 1000); } } From 0535cf025c2c8ceff70ba4bd392d0b8f35c5d082 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Wed, 17 Nov 2021 20:32:41 +0100 Subject: [PATCH 28/71] Remove position log --- .../Components/ReplicaComponents/VehiclePhysicsComponent.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs index e4fb3871..671f1ece 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/VehiclePhysicsComponent.cs @@ -33,7 +33,6 @@ public override VehiclePhysicsConstruction GetConstructPacket() public override VehiclePhysicsSerialization GetSerializePacket() { var packet = base.GetSerializePacket(); - Logger.Information(this.GameObject.Transform.Position); var teleportInfo = new VehicleFrameStatsTeleportInfo { VehicleFrameStats = new VehicleFrameStats From 6436876d77d01ad66f38a23d5a26e9e21ffb6794 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Wed, 17 Nov 2021 20:32:59 +0100 Subject: [PATCH 29/71] Add vehicle death trigger script --- .../Racing/VehicleDeathTrigger.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Uchu.StandardScripts/Racing/VehicleDeathTrigger.cs diff --git a/Uchu.StandardScripts/Racing/VehicleDeathTrigger.cs b/Uchu.StandardScripts/Racing/VehicleDeathTrigger.cs new file mode 100644 index 00000000..f9e42a3d --- /dev/null +++ b/Uchu.StandardScripts/Racing/VehicleDeathTrigger.cs @@ -0,0 +1,30 @@ +using Uchu.World; +using Uchu.World.Scripting.Native; + +namespace Uchu.StandardScripts.Racing; + +[ScriptName("L_ACT_VEHICLE_DEATH_TRIGGER.lua")] +public class VehicleDeathTrigger : ObjectScript +{ + /// + /// Script to kill cars that enter death triggers + /// + /// + public VehicleDeathTrigger(GameObject gameObject) : base(gameObject) + { + if (!gameObject.TryGetComponent(out var physicsComponent)) + return; + + if (!gameObject.Zone.ZoneControlObject.TryGetComponent(out var racingControlComponent)) + return; + + + Listen(physicsComponent.OnEnter, other => + { + if (other.GameObject is not Player player) + return; + + racingControlComponent.OnPlayerRequestDie(player); + }); + } +} From 5ea893944bce54371abf262cd2dc5642c3a1d084 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Wed, 17 Nov 2021 21:54:55 +0100 Subject: [PATCH 30/71] Initialize FV racetrack things to correct position --- .../Racing/ForbiddenValleyColumns.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 Uchu.StandardScripts/Racing/ForbiddenValleyColumns.cs diff --git a/Uchu.StandardScripts/Racing/ForbiddenValleyColumns.cs b/Uchu.StandardScripts/Racing/ForbiddenValleyColumns.cs new file mode 100644 index 00000000..040633c5 --- /dev/null +++ b/Uchu.StandardScripts/Racing/ForbiddenValleyColumns.cs @@ -0,0 +1,15 @@ +using Uchu.World; +using Uchu.World.Scripting.Native; + +namespace Uchu.StandardScripts.Racing; + +[ScriptName("ScriptComponent_1330_script_name__removed")] +public class ForbiddenValleyColumns : ObjectScript +{ + public ForbiddenValleyColumns(GameObject gameObject) : base(gameObject) + { + var movingPlatformComponent = gameObject.GetComponent(); + + movingPlatformComponent.MoveTo(0); + } +} From 281ca2c938ffae5aeea48ca60e407c1379b9b469 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Wed, 17 Nov 2021 21:55:11 +0100 Subject: [PATCH 31/71] Imagination crate script --- .../Racing/ImaginationCrate.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Uchu.StandardScripts/Racing/ImaginationCrate.cs diff --git a/Uchu.StandardScripts/Racing/ImaginationCrate.cs b/Uchu.StandardScripts/Racing/ImaginationCrate.cs new file mode 100644 index 00000000..dfc9ccaa --- /dev/null +++ b/Uchu.StandardScripts/Racing/ImaginationCrate.cs @@ -0,0 +1,25 @@ +using Uchu.World; +using Uchu.World.Scripting.Native; + +namespace Uchu.StandardScripts.Racing; + +[ScriptName("RACE_IMAGINE_CRATE_SERVER.lua")] +public class ImaginationCrate : ObjectScript +{ + public ImaginationCrate(GameObject gameObject) : base(gameObject) + { + this.Listen(gameObject.GetComponent().OnSmashed, (killer, lootOwner) => + { + if (killer is not Player player) + return; + + var car = player.GetComponent().VehicleObject; + if (car is null) + return; + + // it's meant to be skill 585 but i could not get that to work + var destroyableComponent = car.GetComponent(); + destroyableComponent.Imagination += 100; + }); + } +} From be31e344ac060238dcc178b7811a9a026b90e58f Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Wed, 17 Nov 2021 22:01:19 +0100 Subject: [PATCH 32/71] FV racetrack dragon eggs imagination script --- .../Racing/ForbiddenValleyEggs.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs diff --git a/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs b/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs new file mode 100644 index 00000000..53bdd773 --- /dev/null +++ b/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs @@ -0,0 +1,25 @@ +using Uchu.World; +using Uchu.World.Scripting.Native; + +namespace Uchu.StandardScripts.Racing; + +[ScriptName("FV_RACE_SMASH_EGG_IMAGINE_SERVER.lua")] +public class ForbiddenValleyEggs : ObjectScript +{ + public ForbiddenValleyEggs(GameObject gameObject) : base(gameObject) + { + this.Listen(gameObject.GetComponent().OnSmashed, (killer, lootOwner) => + { + if (killer is not Player player) + return; + + var car = player.GetComponent().VehicleObject; + if (car is null) + return; + + // it's meant to be skill 586... + var destroyableComponent = car.GetComponent(); + destroyableComponent.Imagination += 10; + }); + } +} From 9eb7d663695b3a4b5bc3f634346f5850d4c7304e Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Sat, 20 Nov 2021 13:48:38 +0100 Subject: [PATCH 33/71] Remove array to fix ranking, thanks @TecCheck --- .../ReplicaComponents/RacingControlComponent.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index ee5c3ded..595ab67d 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -476,13 +476,12 @@ public override void Serialize(BitWriter writer) packet.RaceInfo = _raceInfo; - packet.DuringRacePlayerInfos = this._players.Select(info => new DuringRacePlayerInfo - { - Player = info.Player, - BestLapTime = (float) info.BestLapTime.TotalSeconds, - RaceTime = (float) info.RaceTime.TotalSeconds - }).ToArray(); - + // packet.DuringRacePlayerInfos = this._players.Select(info => new DuringRacePlayerInfo + // { + // Player = info.Player, + // BestLapTime = (float) info.BestLapTime.TotalSeconds, + // RaceTime = (float) info.RaceTime.TotalSeconds + // }).ToArray(); // TODO From e00328bc60a8e2e75a35c9e2089f2732434f2f55 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Sun, 21 Nov 2021 14:28:15 +0100 Subject: [PATCH 34/71] Add delay to respawn in racing --- .../RacingControlComponent.cs | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 595ab67d..5fb694db 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -410,21 +410,23 @@ public void OnPlayerRequestDie(Player player) Associate = racingPlayer.Vehicle, }, player); - Zone.BroadcastMessage(new RacingSetPlayerResetInfoMessage - { - Associate = GameObject, - CurrentLap = (int) racingPlayer.Lap, - FurthestResetPlane = racingPlayer.RespawnIndex, - PlayerId = player, - RespawnPos = racingPlayer.RespawnPosition + Vector3.UnitY * 5, - UpcomingPlane = racingPlayer.RespawnIndex + 1, - }); + Zone.Schedule(() => { + Zone.BroadcastMessage(new RacingSetPlayerResetInfoMessage + { + Associate = GameObject, + CurrentLap = (int) racingPlayer.Lap, + FurthestResetPlane = racingPlayer.RespawnIndex, + PlayerId = player, + RespawnPos = racingPlayer.RespawnPosition + Vector3.UnitY * 5, + UpcomingPlane = racingPlayer.RespawnIndex + 1, + }); - Zone.BroadcastMessage(new RacingResetPlayerToLastResetMessage - { - Associate = GameObject, - PlayerId = player, - }); + Zone.BroadcastMessage(new RacingResetPlayerToLastResetMessage + { + Associate = GameObject, + PlayerId = player, + }); + }, 2000); } private void PlayerReachedCheckpoint(Player player, int index) From e4abf33b5b88776195c785e182bb260ef2084635 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Sun, 21 Nov 2021 21:32:26 +0100 Subject: [PATCH 35/71] End of race exit button --- .../RacingControlComponent.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 5fb694db..85033ae8 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -106,10 +106,29 @@ private async Task LoadAsync() private void OnMessageBoxRespond(Player player, MessageBoxRespondMessage message) { Logger.Information($"Button - {message.Button} {message.Identifier} {message.UserData}"); - if (message.Identifier == "ACT_RACE_EXIT_THE_RACE?" && message.Button == 1) + if (message.Identifier == "ACT_RACE_EXIT_THE_RACE?" && message.Button == 1 || message.Identifier == "Exit") { + player.Message(new NotifyRacingClientMessage + { + Associate = GameObject, + EventType = RacingClientNotificationType.Exit, + SingleClient = player, + }); + SendPlayerToMainWorld(player); } + else if (message.Identifier == "rewardButton") + { + // TODO: send rewards - relies on activityID being present in object settings + // await this.DropLootAsync(player); + + player.Message(new NotifyRacingClientMessage + { + Associate = GameObject, + EventType = RacingClientNotificationType.RewardPlayer, + SingleClient = player, + }); + } } /// From 69a1ecac8f8d3494d50e922d435aa29a8d515ca1 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Sun, 21 Nov 2021 21:33:09 +0100 Subject: [PATCH 36/71] Better timing at start of race --- .../RacingControlComponent.cs | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 85033ae8..3014dab8 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -276,23 +276,6 @@ private void InitRace() _racingStatus = RacingStatus.Loaded; - Zone.BroadcastMessage(new NotifyRacingClientMessage - { - Associate = this.GameObject, - EventType = RacingClientNotificationType.ActivityStart, - }); - - // Start after 6 seconds - Zone.Schedule(StartRace, 6000); - } - - private void StartRace() - { - if (_racingStatus != RacingStatus.Loaded) - return; - - _racingStatus = RacingStatus.Started; - // Start imagination spawners var minSpawner = Zone.SpawnerNetworks.FirstOrDefault(gameObject => gameObject.Name == "ImaginationSpawn_Min"); var medSpawner = Zone.SpawnerNetworks.FirstOrDefault(gameObject => gameObject.Name == "ImaginationSpawn_Med"); @@ -346,6 +329,27 @@ private void StartRace() }); } + Zone.BroadcastMessage(new NotifyRacingClientMessage + { + Associate = this.GameObject, + EventType = RacingClientNotificationType.ActivityStart, + }); + + // Start after 7 seconds + Task.Run(async () => + { + await Task.Delay(7000); + this.StartRace(); + }); + } + + private void StartRace() + { + if (_racingStatus != RacingStatus.Loaded) + return; + + _racingStatus = RacingStatus.Started; + // Go! foreach (var info in _players) { From d2477ae5679690574ffa86bb59d8600bbb5f2726 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Mon, 22 Nov 2021 20:19:27 +0100 Subject: [PATCH 37/71] Avoid null reference in imagination handler --- Uchu.World/Handlers/GameMessages/RacingHandler.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Uchu.World/Handlers/GameMessages/RacingHandler.cs b/Uchu.World/Handlers/GameMessages/RacingHandler.cs index 3e3ce8b3..5c49631a 100644 --- a/Uchu.World/Handlers/GameMessages/RacingHandler.cs +++ b/Uchu.World/Handlers/GameMessages/RacingHandler.cs @@ -20,8 +20,12 @@ public void VehicleSetWheelLockStateHandler(VehicleSetWheelLockStateMessage mess [PacketHandler] public async void ImaginationHandler(VehicleNotifyHitImaginationServerMessage message, Player player) { - message.Associate.GetComponent().Imagination += 10; - await message.PickupObjId.GetComponent().SmashAsync(message.Associate); + if (message.Associate is null || message.PickupObjId is null) + return; + if (message.Associate.TryGetComponent(out var vehicleDestroyableComponent)) + vehicleDestroyableComponent.Imagination += 10; + if (message.PickupObjId.TryGetComponent(out var imaginationDestructibleComponent)) + await imaginationDestructibleComponent.SmashAsync(message.Associate); } [PacketHandler] From b9d89dd994e88d216983c27534ab0b5cc854ceb1 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Tue, 23 Nov 2021 15:07:40 +0100 Subject: [PATCH 38/71] Fix match queue display --- Uchu.World/Systems/Match/MatchInstance.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Uchu.World/Systems/Match/MatchInstance.cs b/Uchu.World/Systems/Match/MatchInstance.cs index 4cb51932..2b1dc2af 100644 --- a/Uchu.World/Systems/Match/MatchInstance.cs +++ b/Uchu.World/Systems/Match/MatchInstance.cs @@ -222,8 +222,8 @@ public void AddPlayer(Player player) Associate = player, Data = new LegoDataDictionary { - {"player", player.Id, 9}, - {"playerName", player.Name, 0}, + {"player", otherPlayer.Id, 9}, + {"playerName", otherPlayer.Name, 0}, }, Type = MatchUpdateType.PlayerAdded, }); From 49745c28faa579601db33a698e8ee46c31829205 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Wed, 24 Nov 2021 11:54:08 +0100 Subject: [PATCH 39/71] Send players to match instance when zone is loaded --- Uchu.Api/Models/ZoneStatusResponse.cs | 7 +++++ Uchu.World/Api/WorldCommands.cs | 35 ++++++++++++++++++++++- Uchu.World/Systems/Match/MatchInstance.cs | 34 ++++++++++++++++++---- 3 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 Uchu.Api/Models/ZoneStatusResponse.cs diff --git a/Uchu.Api/Models/ZoneStatusResponse.cs b/Uchu.Api/Models/ZoneStatusResponse.cs new file mode 100644 index 00000000..fd6a8422 --- /dev/null +++ b/Uchu.Api/Models/ZoneStatusResponse.cs @@ -0,0 +1,7 @@ +namespace Uchu.Api.Models +{ + public class ZoneStatusResponse : BaseResponse + { + public bool Loaded { get; set; } + } +} diff --git a/Uchu.World/Api/WorldCommands.cs b/Uchu.World/Api/WorldCommands.cs index 3c1948c6..1d338f91 100644 --- a/Uchu.World/Api/WorldCommands.cs +++ b/Uchu.World/Api/WorldCommands.cs @@ -46,6 +46,39 @@ public object ZonePlayers(string zone) return response; } + /// + /// Take in a zone ID, find that zone in this server's Zones, return whether it is fully loaded + /// + /// Zone ID + /// Whether the zone has loaded all objects + [ApiCommand("world/zoneStatus")] + public object ZoneStatus(string zone) + { + var response = new ZoneStatusResponse(); + + if (!int.TryParse(zone, out var zoneId)) + { + response.FailedReason = "invalid zone"; + + return response; + } + + var zoneInstance = UchuServer.Zones.FirstOrDefault(z => z.ZoneId == (ZoneId) zoneId); + + if (zoneInstance == default) + { + response.FailedReason = "not found"; + + return response; + } + + response.Success = true; + + response.Loaded = zoneInstance.Loaded; + + return response; + } + [ApiCommand("world/saveAndKick")] public async Task SaveAndKick() { @@ -75,4 +108,4 @@ public async Task Announce(string title, string message) } } } -} \ No newline at end of file +} diff --git a/Uchu.World/Systems/Match/MatchInstance.cs b/Uchu.World/Systems/Match/MatchInstance.cs index 2b1dc2af..575dbe03 100644 --- a/Uchu.World/Systems/Match/MatchInstance.cs +++ b/Uchu.World/Systems/Match/MatchInstance.cs @@ -105,11 +105,35 @@ public MatchInstance(int type, Zone zone) return; } - // Start the match. - foreach (var player in _players) + const int checkEveryMs = 2000; + const int timeoutMs = 30000; + var zoneLoadedTimer = new Timer(checkEveryMs); + var elapsed = 0; + zoneLoadedTimer.Elapsed += async (o, eventArgs) => { - await player.SendToWorldAsync(allocatedInstance, (ZoneId) matchZoneId); - } + elapsed += checkEveryMs; + if (elapsed >= timeoutMs) + { + zoneLoadedTimer.Stop(); + zoneLoadedTimer.Dispose(); + return; + } + + var status = await zone.Server.Api.RunCommandAsync( + allocatedInstance.ApiPort, $"world/zoneStatus?id={matchZoneId}" + ).ConfigureAwait(false); + + // If the zone is ready, start the match. + if (!status.Success || !status.Loaded) return; + zoneLoadedTimer.Stop(); + zoneLoadedTimer.Dispose(); + + // Start the match. + foreach (var player in _players) + { + await player.SendToWorldAsync(allocatedInstance, (ZoneId) matchZoneId); + } + }; }; } @@ -334,4 +358,4 @@ public void SetPlayerNotReady(Player player) } } } -} \ No newline at end of file +} From 841f454873204a1b218926a1c100ad43f6e8c8fe Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Fri, 26 Nov 2021 18:51:56 +0100 Subject: [PATCH 40/71] Actually start timer --- Uchu.World/Systems/Match/MatchInstance.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Uchu.World/Systems/Match/MatchInstance.cs b/Uchu.World/Systems/Match/MatchInstance.cs index 575dbe03..93484d0f 100644 --- a/Uchu.World/Systems/Match/MatchInstance.cs +++ b/Uchu.World/Systems/Match/MatchInstance.cs @@ -134,6 +134,7 @@ public MatchInstance(int type, Zone zone) await player.SendToWorldAsync(allocatedInstance, (ZoneId) matchZoneId); } }; + zoneLoadedTimer.Start(); }; } From 35fee934cf86f0dc0df2fa7d9b319ec6f98ad20e Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Fri, 26 Nov 2021 20:48:39 +0100 Subject: [PATCH 41/71] Remove some unnecessary usages of .ToArray() --- Uchu.World/Objects/GameObjects/Player.cs | 3 +-- Uchu.World/Objects/Perspective/Perspective.cs | 2 ++ Uchu.World/Objects/Zone.cs | 8 ++------ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/Uchu.World/Objects/GameObjects/Player.cs b/Uchu.World/Objects/GameObjects/Player.cs index 634d5b38..f312093c 100644 --- a/Uchu.World/Objects/GameObjects/Player.cs +++ b/Uchu.World/Objects/GameObjects/Player.cs @@ -391,7 +391,6 @@ public void Teleport(Vector3 position, Quaternion? rotation = null, bool ignore /// internal void UpdateView() { - var loadedObjects = Perspective.LoadedObjects.ToArray(); foreach (var gameObject in Zone.Spawned) { TriggerViewUpdate(gameObject); @@ -404,7 +403,7 @@ internal void UpdateView() /// The game object to check public void TriggerViewUpdate(GameObject gameObject) { - var spawned = Perspective.LoadedObjects.ToArray().Contains(gameObject); + var spawned = Perspective.IsLoaded(gameObject); var view = Perspective.View(gameObject); if (!spawned && view) diff --git a/Uchu.World/Objects/Perspective/Perspective.cs b/Uchu.World/Objects/Perspective/Perspective.cs index d816b973..d45a7fc1 100644 --- a/Uchu.World/Objects/Perspective/Perspective.cs +++ b/Uchu.World/Objects/Perspective/Perspective.cs @@ -15,6 +15,8 @@ public class Perspective public GameObject[] LoadedObjects => _networkDictionary.Keys.ToArray(); + public bool IsLoaded(GameObject gameObject) => _networkDictionary.ContainsKey(gameObject); + public MaskFilter MaskFilter => TryGetFilter(out var filter) ? filter : default; public RenderDistanceFilter RenderDistanceFilter => TryGetFilter(out var filter) ? filter : default; diff --git a/Uchu.World/Objects/Zone.cs b/Uchu.World/Objects/Zone.cs index 39a81780..a94a2952 100644 --- a/Uchu.World/Objects/Zone.cs +++ b/Uchu.World/Objects/Zone.cs @@ -287,7 +287,7 @@ private async Task LoadObjects() { Logger.Debug($"Trigger: {trigger}"); } - + Logger.Debug($"Loading {levelObject.Lot} [{levelObject.ObjectId}]..."); try @@ -761,11 +761,7 @@ private void UpdateObjects() var watch = new Stopwatch(); watch.Start(); - var updatedObjects = UpdatedObjects.ToArray(); - var visibleObjects = updatedObjects.Select(o => o.Associate) - .Intersect(Players.SelectMany(p => p.Perspective.LoadedObjects)).ToHashSet(); - var objectsToUpdate = updatedObjects - .Where(o => visibleObjects.Contains(o.Associate)); + var objectsToUpdate = UpdatedObjects.Where(o => Players.Any(p => p.Perspective.IsLoaded(o.Associate as GameObject))); foreach (var updatedObject in objectsToUpdate) { From c2f5e40ec565dd4e829081417e376727c889a8a5 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Fri, 26 Nov 2021 20:49:44 +0100 Subject: [PATCH 42/71] Disable logging every message sent/received --- Uchu.Core/Extensions/RakConnectionExtensions.cs | 6 +++--- Uchu.Core/UchuServer.cs | 2 +- Uchu.World/WorldUchuServer.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Uchu.Core/Extensions/RakConnectionExtensions.cs b/Uchu.Core/Extensions/RakConnectionExtensions.cs index 38dbe5c9..1235d4ab 100644 --- a/Uchu.Core/Extensions/RakConnectionExtensions.cs +++ b/Uchu.Core/Extensions/RakConnectionExtensions.cs @@ -16,7 +16,7 @@ public static void Send(this IRakConnection @this, ISerializable serializable) if (serializable == null) throw new ArgumentNullException(nameof(serializable), ResourceStrings.RakConnectionExtensions_Send_StreamNullException); - Logger.Debug($"Sending {serializable}"); + // Logger.Debug($"Sending {serializable}"); using var stream = new MemoryStream(); using var writer = new BitWriter(stream); @@ -57,7 +57,7 @@ public static void Send(this IRakConnection @this, T packet) where T : struct if (@this == null) throw new ArgumentNullException(nameof(@this), ResourceStrings.RakConnectionExtensions_Send_ConnectionNullException); - Logger.Debug($"Sending {packet}"); + // Logger.Debug($"Sending {packet}"); try { @@ -69,4 +69,4 @@ public static void Send(this IRakConnection @this, T packet) where T : struct } } } -} \ No newline at end of file +} diff --git a/Uchu.Core/UchuServer.cs b/Uchu.Core/UchuServer.cs index 97d105ee..8b3de7d6 100644 --- a/Uchu.Core/UchuServer.cs +++ b/Uchu.Core/UchuServer.cs @@ -508,7 +508,7 @@ public async Task HandlePacketAsync(IPEndPoint endPoint, byte[] data, Reliabilit return; } - Logger.Debug($"Received {handler.PacketType.FullName}"); + // Logger.Debug($"Received {handler.PacketType.FullName}"); reader.BaseStream.Position = 8; try diff --git a/Uchu.World/WorldUchuServer.cs b/Uchu.World/WorldUchuServer.cs index 26562ea4..026d2019 100644 --- a/Uchu.World/WorldUchuServer.cs +++ b/Uchu.World/WorldUchuServer.cs @@ -275,7 +275,7 @@ private async Task HandleGameMessageAsync(long objectId, ushort messageId, BitRe return; } - Logger.Debug($"Received {(GameMessageId)messageId}"); + // Logger.Debug($"Received {(GameMessageId)messageId}"); // Check if this message came from a logged in player var player = Zones.SelectMany(z => z.Players).FirstOrDefault(p => p.Connection.Equals(connection)); From aa4d594eccc29e27534e199f5d6ce16fbc711b93 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Tue, 7 Dec 2021 20:08:35 +0100 Subject: [PATCH 43/71] Add RacingTask --- .../Handlers/GameMessages/RacingHandler.cs | 5 + .../Player/MissionInventoryComponent.cs | 81 +++++++++ .../RacingControlComponent.cs | 3 + .../Systems/Missions/MissionInstance.cs | 1 + Uchu.World/Systems/Missions/RacingTask.cs | 154 ++++++++++++++++++ 5 files changed, 244 insertions(+) create mode 100644 Uchu.World/Systems/Missions/RacingTask.cs diff --git a/Uchu.World/Handlers/GameMessages/RacingHandler.cs b/Uchu.World/Handlers/GameMessages/RacingHandler.cs index 5c49631a..8c3518cd 100644 --- a/Uchu.World/Handlers/GameMessages/RacingHandler.cs +++ b/Uchu.World/Handlers/GameMessages/RacingHandler.cs @@ -26,6 +26,11 @@ public async void ImaginationHandler(VehicleNotifyHitImaginationServerMessage me vehicleDestroyableComponent.Imagination += 10; if (message.PickupObjId.TryGetComponent(out var imaginationDestructibleComponent)) await imaginationDestructibleComponent.SmashAsync(message.Associate); + + // Progress imagination collect task + if (message.Associate.TryGetComponent(out PossessableComponent possessableComponent)) + if (possessableComponent.Driver.TryGetComponent(out MissionInventoryComponent missionInventoryComponent)) + await missionInventoryComponent.RacingCollectImaginationAsync(); } [PacketHandler] diff --git a/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs b/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs index 7fc5c5ff..9335ca67 100644 --- a/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs +++ b/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs @@ -706,6 +706,87 @@ await StartUnlockableAchievementsAsync(MissionTaskType.TamePet, Pet }); } + public async Task RaceFinishedAsync(int rank, int racetime) + { + foreach (var task in FindActiveTasksAsync()) + { + await task.ReportRank(rank, this.GameObject.Zone.ZoneId); + await task.ReportRacetime(racetime, this.GameObject.Zone.ZoneId); + if (rank == 1) await task.ReportWin(this.GameObject.Zone.ZoneId); + } + + await StartUnlockableAchievementsAsync(MissionTaskType.Racing, 0, async task => + { + await task.ReportRank(rank, this.GameObject.Zone.ZoneId); + await task.ReportRacetime(racetime, this.GameObject.Zone.ZoneId); + }); + } + + public async Task RacingLaptimeAsync(int laptime) + { + foreach (var task in FindActiveTasksAsync()) + { + await task.ReportLaptime(laptime, this.GameObject.Zone.ZoneId); + } + + await StartUnlockableAchievementsAsync(MissionTaskType.Racing, laptime, async task => + { + await task.ReportLaptime(laptime, this.GameObject.Zone.ZoneId); + }); + } + + public async Task RacingMissionCompleteAsync(int missionId) + { + foreach (var task in FindActiveTasksAsync()) + { + await task.ReportMissionComplete(missionId); + } + + await StartUnlockableAchievementsAsync(MissionTaskType.Racing, missionId, async task => + { + await task.ReportMissionComplete(missionId); + }); + } + + public async Task RacingCollectImaginationAsync() + { + foreach (var task in FindActiveTasksAsync()) + { + await task.ReportImagination(this.GameObject.Zone.ZoneId); + } + + await StartUnlockableAchievementsAsync(MissionTaskType.Racing, Lot.Imagination, async task => + { + await task.ReportImagination(this.GameObject.Zone.ZoneId); + }); + } + + public async Task RacingEnterWorld(ZoneId zoneId) + { + foreach (var task in FindActiveTasksAsync()) + { + await task.ReportWorldEnter(zoneId); + } + + await StartUnlockableAchievementsAsync(MissionTaskType.Racing, (int)zoneId, async task => + { + await task.ReportImagination(zoneId); + }); + } + + public async Task RacingSmashAsync(Lot lot) + { + foreach (var task in FindActiveTasksAsync()) + { + await task.ReportSmash(lot); + } + + await StartUnlockableAchievementsAsync(MissionTaskType.Racing, lot, async task => + { + await task.ReportSmash(lot);; + }); + } + /// /// Returns a list of achievements that a player may start for a certain task type due to meeting it's prerequisites /// diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 3014dab8..51600648 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -146,6 +146,9 @@ private void OnPlayerLoad(Player player) return; } + if (player.TryGetComponent(out MissionInventoryComponent missionInventoryComponent)) + missionInventoryComponent.RacingEnterWorld(this.GameObject.Zone.ZoneId); + // Register player this._players.Add(new RacingPlayerInfo { diff --git a/Uchu.World/Systems/Missions/MissionInstance.cs b/Uchu.World/Systems/Missions/MissionInstance.cs index 17c353a2..bdc9a9e1 100644 --- a/Uchu.World/Systems/Missions/MissionInstance.cs +++ b/Uchu.World/Systems/Missions/MissionInstance.cs @@ -276,6 +276,7 @@ static MissionInstance() [MissionTaskType.Discover] = typeof(DiscoverTask), [MissionTaskType.MinigameAchievement] = typeof(MinigameAchievementTask), [MissionTaskType.CollectPowerup] = typeof(CollectPowerupTask), + [MissionTaskType.Racing] = typeof(RacingTask), }; } diff --git a/Uchu.World/Systems/Missions/RacingTask.cs b/Uchu.World/Systems/Missions/RacingTask.cs new file mode 100644 index 00000000..e772a3cb --- /dev/null +++ b/Uchu.World/Systems/Missions/RacingTask.cs @@ -0,0 +1,154 @@ +using System.Linq; +using System.Threading.Tasks; +using Uchu.Core; + +namespace Uchu.World.Systems.Missions +{ + public class RacingTask : MissionTaskInstance + { + public RacingTask(MissionInstance mission, int taskId, int missionTaskIndex) + : base(mission, taskId, missionTaskIndex) + { + } + + public override MissionTaskType Type => MissionTaskType.Racing; + + public override bool Completed => IsCompleted(); + + public async Task ReportRank(int place, ZoneId zoneId) + { + if (!IsValid(TaskType.Rank, zoneId)) + return; + + await ReportProgressValue(place, zoneId); + } + + public async Task ReportLaptime(int laptime, ZoneId zoneId) + { + if (!IsValid(TaskType.Laptime, zoneId)) + return; + + await ReportProgressValue(laptime, zoneId); + } + + public async Task ReportRacetime(int racetime, ZoneId zoneId) + { + if (!IsValid(TaskType.Racetime, zoneId)) + return; + + await ReportProgressValue(racetime, zoneId); + } + + private async Task ReportProgressValue(int value, ZoneId zoneId) + { + if (value >= RequiredProgress) + AddProgress(zoneId); + + if (Completed) + await CheckMissionCompletedAsync(); + } + + public async Task ReportMissionComplete(int missionId) + { + if (!IsValid(TaskType.Achievements, missionId)) + return; + + AddProgress(missionId); + + if (Completed) + await CheckMissionCompletedAsync(); + } + + public async Task ReportWreck(int wrecks, ZoneId zoneId) + { + if (!IsValid(TaskType.Wrecks, zoneId)) + return; + + if (wrecks >= RequiredProgress) + return; + + AddProgress(zoneId); + + if (Completed) + await CheckMissionCompletedAsync(); + } + + public async Task ReportImagination(ZoneId zoneId) + { + if (!IsValid(TaskType.Imagination, zoneId)) + return; + + AddProgress(zoneId); + + if (Completed) + await CheckMissionCompletedAsync(); + } + + public async Task ReportWorldEnter(ZoneId zoneId) + { + if (!IsValid(TaskType.EnterWorld, zoneId)) + return; + + AddProgress(zoneId); + + if (Completed) + await CheckMissionCompletedAsync(); + } + + public async Task ReportWin(ZoneId zoneId) + { + if (Parameters[0] != (int)TaskType.WinInWorld && Parameters[0] != (int)TaskType.WinInWorlds) + return; + + if (!Targets.Contains(zoneId)) + return; + + AddProgress(zoneId); + + if (Completed) + await CheckMissionCompletedAsync(); + } + + public async Task ReportSmash(Lot lot) + { + if (!IsValid(TaskType.Smash, lot)) + return; + + AddProgress(lot); + + if (Completed) + await CheckMissionCompletedAsync(); + } + + private bool IsCompleted() + { + switch ((TaskType)Parameters[0]) + { + case TaskType.Rank: + case TaskType.Laptime: + case TaskType.Racetime: + return Progress.Contains(Target); + + default: + return base.Completed; + } + } + + private bool IsValid(TaskType taskType, int target) => + Parameters[0] == (int)taskType && Targets.Contains(target); + + private enum TaskType : int + { + Rank = 1, + Laptime = 2, + Racetime = 3, + Achievements = 5, + Wrecks = 10, + Imagination = 12, + EnterWorld = 13, + WinInWorld = 14, + WinInWorlds = 15, + Smash = 17 + } + } +} From aa11f5ddc18a3cb427f5e8f3ef6d0e81f5716512 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Sun, 12 Dec 2021 15:17:14 +0100 Subject: [PATCH 44/71] Implement timing and ranking for racing --- .../RacingControlComponent.cs | 64 ++++++++++++++++--- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 51600648..dda015bd 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -1,9 +1,14 @@ // Thanks to Simon Nitzsche for his research into racing // https://github.com/SimonNitzsche/OpCrux-Server/ // https://www.youtube.com/watch?v=X5qvEDmtE5U +// +// Thanks to Darkflame Universe for releasing the source code +// of their server. https://github.com/DarkflameUniverse/ +// https://www.darkflameuniverse.org/ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Numerics; using System.Threading.Tasks; @@ -30,6 +35,8 @@ public class RacingControlComponent : ScriptedActivityComponent private MainWorldReturnData _returnData; + private int _rankCounter = 0; + public RacingControlComponent() { _raceInfo.LapCount = 3; @@ -156,6 +163,9 @@ private void OnPlayerLoad(Player player) PlayerLoaded = true, PlayerIndex = (uint) _players.Count + 1, NoSmashOnReload = true, + RaceTime = new Stopwatch(), + LapTime = new Stopwatch(), + BestLapTime = new TimeSpan(0), }); LoadPlayerCar(player); @@ -322,6 +332,7 @@ private void InitRace() if (playersInPhysicsObject.Contains(player)) return; playersInPhysicsObject.Add(player); + // TODO: Check for players driving in the wrong direction this.PlayerReachedCheckpoint(player, index); }); this.Listen(physics.OnLeave, component => @@ -356,6 +367,9 @@ private void StartRace() // Go! foreach (var info in _players) { + info.RaceTime.Restart(); + info.LapTime.Restart(); + Zone.BroadcastMessage(new VehicleUnlockInputMessage { Associate = info.Vehicle, @@ -460,6 +474,40 @@ private void PlayerReachedCheckpoint(Player player, int index) var waypoint = _path.Waypoints[index]; var playerInfoIndex = _players.FindIndex(x => x.Player == player); var playerInfo = _players[playerInfoIndex]; + + // If start point is reached + if (index == 0) + { + var lapTime = (int)playerInfo.LapTime.ElapsedMilliseconds; + playerInfo.LapTime.Restart(); + + // If player finished race + if (playerInfo.Lap >= _raceInfo.LapCount) + { + var raceTime = (int)playerInfo.RaceTime.ElapsedMilliseconds; + playerInfo.RaceTime.Stop(); + + if (playerInfo.Player.TryGetComponent(out MissionInventoryComponent missionInventoryComponent)) + missionInventoryComponent.RaceFinishedAsync(++_rankCounter, (int)playerInfo.RaceTime.ElapsedMilliseconds); + } + + // Set new best lap if applicable + if (lapTime > playerInfo.BestLapTime.Milliseconds) + { + playerInfo.BestLapTime = new TimeSpan(0, 0, 0, 0, lapTime); + + if (playerInfo.Player.TryGetComponent(out MissionInventoryComponent missionInventoryComponent)) + missionInventoryComponent.RacingLaptimeAsync(lapTime); + } + + playerInfo.Lap++; + + Logger.Debug("Player crossed line: " + lapTime); + Logger.Debug("Now Lap " + playerInfo.Lap); + + // TODO: Update Leaderboard + } + playerInfo.RespawnIndex = (uint) index; playerInfo.RespawnPosition = waypoint.Position; playerInfo.RespawnRotation = player.Transform.Rotation; @@ -504,12 +552,12 @@ public override void Serialize(BitWriter writer) packet.RaceInfo = _raceInfo; - // packet.DuringRacePlayerInfos = this._players.Select(info => new DuringRacePlayerInfo - // { - // Player = info.Player, - // BestLapTime = (float) info.BestLapTime.TotalSeconds, - // RaceTime = (float) info.RaceTime.TotalSeconds - // }).ToArray(); + packet.DuringRacePlayerInfos = this._players.Select(info => new DuringRacePlayerInfo + { + Player = info.Player, + BestLapTime = (float) info.BestLapTime.TotalSeconds, + RaceTime = (float) info.RaceTime.Elapsed.TotalSeconds, + }).ToArray(); // TODO @@ -541,11 +589,11 @@ struct RacingPlayerInfo { public uint Finished { get; set; } public uint ReachedPoints { get; set; } public TimeSpan BestLapTime { get; set; } - public TimeSpan LapTime { get; set; } + public Stopwatch LapTime { get; set; } public uint SmashedTimes { get; set; } public bool NoSmashOnReload { get; set; } public bool CollectedRewards { get; set; } - public TimeSpan RaceTime { get; set; } + public Stopwatch RaceTime { get; set; } }; } } From d014a4f939d1c02b047fc1a3540a241a9c8135f8 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Sun, 12 Dec 2021 15:35:47 +0100 Subject: [PATCH 45/71] Fix RacingTask --- .../Objects/Components/Player/MissionInventoryComponent.cs | 1 + Uchu.World/Systems/Missions/RacingTask.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs b/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs index 9335ca67..4109b21e 100644 --- a/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs +++ b/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs @@ -719,6 +719,7 @@ await StartUnlockableAchievementsAsync(MissionTaskType.Racing, 0, as { await task.ReportRank(rank, this.GameObject.Zone.ZoneId); await task.ReportRacetime(racetime, this.GameObject.Zone.ZoneId); + if (rank == 1) await task.ReportWin(this.GameObject.Zone.ZoneId); }); } diff --git a/Uchu.World/Systems/Missions/RacingTask.cs b/Uchu.World/Systems/Missions/RacingTask.cs index e772a3cb..51879d34 100644 --- a/Uchu.World/Systems/Missions/RacingTask.cs +++ b/Uchu.World/Systems/Missions/RacingTask.cs @@ -41,7 +41,7 @@ public async Task ReportRacetime(int racetime, ZoneId zoneId) private async Task ReportProgressValue(int value, ZoneId zoneId) { - if (value >= RequiredProgress) + if (value <= RequiredProgress) AddProgress(zoneId); if (Completed) From c63f8ea9b0f94d2810abc45dd5703453f51a3d80 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Mon, 10 Jan 2022 20:24:53 +0100 Subject: [PATCH 46/71] Implement more stuff in lap routine --- .../Player/MissionInventoryComponent.cs | 4 +- .../RacingControlComponent.cs | 58 ++++++++++++------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs b/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs index 4109b21e..a9b414ce 100644 --- a/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs +++ b/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs @@ -706,12 +706,13 @@ await StartUnlockableAchievementsAsync(MissionTaskType.TamePet, Pet }); } - public async Task RaceFinishedAsync(int rank, int racetime) + public async Task RaceFinishedAsync(int rank, int racetime, int wrecks) { foreach (var task in FindActiveTasksAsync()) { await task.ReportRank(rank, this.GameObject.Zone.ZoneId); await task.ReportRacetime(racetime, this.GameObject.Zone.ZoneId); + await task.ReportWreck(wrecks, this.GameObject.Zone.ZoneId); if (rank == 1) await task.ReportWin(this.GameObject.Zone.ZoneId); } @@ -719,6 +720,7 @@ await StartUnlockableAchievementsAsync(MissionTaskType.Racing, 0, as { await task.ReportRank(rank, this.GameObject.Zone.ZoneId); await task.ReportRacetime(racetime, this.GameObject.Zone.ZoneId); + await task.ReportWreck(wrecks, this.GameObject.Zone.ZoneId); if (rank == 1) await task.ReportWin(this.GameObject.Zone.ZoneId); }); } diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index dda015bd..7df0f4e0 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -35,7 +35,7 @@ public class RacingControlComponent : ScriptedActivityComponent private MainWorldReturnData _returnData; - private int _rankCounter = 0; + private uint _rankCounter = 0; public RacingControlComponent() { @@ -326,14 +326,14 @@ private void InitRace() // Listen for players entering and leaving. var playersInPhysicsObject = new List(); var index = i; - this.Listen(physics.OnEnter, component => + this.Listen(physics.OnEnter, async component => { if (!(component.GameObject is Player player)) return; if (playersInPhysicsObject.Contains(player)) return; playersInPhysicsObject.Add(player); // TODO: Check for players driving in the wrong direction - this.PlayerReachedCheckpoint(player, index); + await this.PlayerReachedCheckpoint(player, index); }); this.Listen(physics.OnLeave, component => { @@ -367,8 +367,8 @@ private void StartRace() // Go! foreach (var info in _players) { - info.RaceTime.Restart(); - info.LapTime.Restart(); + info.RaceTime.Start(); + info.LapTime.Start(); Zone.BroadcastMessage(new VehicleUnlockInputMessage { @@ -439,6 +439,7 @@ private void OnRacingPlayerInfoResetFinished(Player player) public void OnPlayerRequestDie(Player player) { var racingPlayer = _players.Find(info => info.Player == player); + racingPlayer.SmashedTimes++; Logger.Information($"{player} requested death - respawning to {racingPlayer.RespawnPosition}"); @@ -469,26 +470,50 @@ public void OnPlayerRequestDie(Player player) }, 2000); } - private void PlayerReachedCheckpoint(Player player, int index) + private async Task PlayerReachedCheckpoint(Player player, int index) { var waypoint = _path.Waypoints[index]; var playerInfoIndex = _players.FindIndex(x => x.Player == player); var playerInfo = _players[playerInfoIndex]; - // If start point is reached - if (index == 0) + // Only count up + // TODO: reset player + if (playerInfo.RespawnIndex > index && index != 0) + return; + + playerInfo.RespawnIndex = (uint)index; + playerInfo.RespawnPosition = waypoint.Position; + playerInfo.RespawnRotation = player.Transform.Rotation; + + // If start point is reached (with 40 sec cheat protection) + if (index == 0 && playerInfo.LapTime.ElapsedMilliseconds > 40000) { var lapTime = (int)playerInfo.LapTime.ElapsedMilliseconds; playerInfo.LapTime.Restart(); + playerInfo.Lap++; + Logger.Information($"{playerInfo.Player} now in lap {playerInfo.Lap}"); + // If player finished race if (playerInfo.Lap >= _raceInfo.LapCount) { var raceTime = (int)playerInfo.RaceTime.ElapsedMilliseconds; playerInfo.RaceTime.Stop(); - + playerInfo.Finished = ++_rankCounter; + if (playerInfo.Player.TryGetComponent(out MissionInventoryComponent missionInventoryComponent)) - missionInventoryComponent.RaceFinishedAsync(++_rankCounter, (int)playerInfo.RaceTime.ElapsedMilliseconds); + await missionInventoryComponent.RaceFinishedAsync((int)playerInfo.Finished, raceTime, (int)playerInfo.SmashedTimes); + + Logger.Information($"Race finished: {playerInfo.Player}, place {playerInfo.Finished}, time: {playerInfo.RaceTime.ElapsedMilliseconds}, smashed: {playerInfo.SmashedTimes}"); + + // TODO: Give rewards + // TODO: Update leaderboard + + if (_rankCounter >= _players.Count) + { + Logger.Information("Race ended"); + // TODO: close instance + } } // Set new best lap if applicable @@ -497,22 +522,11 @@ private void PlayerReachedCheckpoint(Player player, int index) playerInfo.BestLapTime = new TimeSpan(0, 0, 0, 0, lapTime); if (playerInfo.Player.TryGetComponent(out MissionInventoryComponent missionInventoryComponent)) - missionInventoryComponent.RacingLaptimeAsync(lapTime); + await missionInventoryComponent.RacingLaptimeAsync(lapTime); } - - playerInfo.Lap++; - - Logger.Debug("Player crossed line: " + lapTime); - Logger.Debug("Now Lap " + playerInfo.Lap); - - // TODO: Update Leaderboard } - playerInfo.RespawnIndex = (uint) index; - playerInfo.RespawnPosition = waypoint.Position; - playerInfo.RespawnRotation = player.Transform.Rotation; _players[playerInfoIndex] = playerInfo; - Logger.Information($"Player reached checkpoint: {index}"); } From 75433d8a3704358e35a1ab85b8f6d030a0811714 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Thu, 3 Feb 2022 11:43:40 +0100 Subject: [PATCH 47/71] Add racing leaderboard --- Uchu.Core/Database/Models/ActivityScore.cs | 7 ++ .../Handlers/GameMessages/MatchHandler.cs | 15 ++- .../RacingControlComponent.cs | 107 ++++++++++++++++-- 3 files changed, 117 insertions(+), 12 deletions(-) diff --git a/Uchu.Core/Database/Models/ActivityScore.cs b/Uchu.Core/Database/Models/ActivityScore.cs index 92c53c96..13dd2b4c 100644 --- a/Uchu.Core/Database/Models/ActivityScore.cs +++ b/Uchu.Core/Database/Models/ActivityScore.cs @@ -18,12 +18,19 @@ public class ActivityScore public int Points { get; set; } + // BestTime for racing public int Time { get; set; } public long LastPlayed { get; set; } public int NumPlayed { get; set; } + // Racing + public int BestLapTime { get; set; } + + // Racing + public int Wins { get; set; } + // YYYYWW for Weekly leaderboard entries // 0 for All-time leaderboard entries public int Week { get; set; } diff --git a/Uchu.World/Handlers/GameMessages/MatchHandler.cs b/Uchu.World/Handlers/GameMessages/MatchHandler.cs index d5f5ab7e..e62ee70e 100644 --- a/Uchu.World/Handlers/GameMessages/MatchHandler.cs +++ b/Uchu.World/Handlers/GameMessages/MatchHandler.cs @@ -142,9 +142,20 @@ public void RequestActivitySummaryLeaderboardDataMessageHandler(RequestActivityS data.Add($"Result[0].Row[{index}].LastPlayed", activityScore.LastPlayed); data.Add($"Result[0].Row[{index}].NumPlayed", activityScore.NumPlayed); data.Add($"Result[0].Row[{index}].RowNumber", rank); - data.Add($"Result[0].Row[{index}].Time", activityScore.Time); - data.Add($"Result[0].Row[{index}].Points", activityScore.Points); data.Add($"Result[0].Row[{index}].name", characterName); + + if (leaderboardType == LeaderboardType.Racing) + { + data.Add($"Result[0].Row[{index}].BestTime", activityScore.Time); + data.Add($"Result[0].Row[{index}].BestLapTime", activityScore.BestLapTime); + data.Add($"Result[0].Row[{index}].NumWins", activityScore.Wins); + } + else + { + data.Add($"Result[0].Row[{index}].Time", activityScore.Time); + data.Add($"Result[0].Row[{index}].Points", activityScore.Points); + } + // TODO: ".Relationship" variable (int). // (AGS client script: if not 0, FoundFriendGuild set to true. Teams?) // data.Add($"Result[0].Row[{index}].Relationship", 0); diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 7df0f4e0..44be6021 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Numerics; using System.Threading.Tasks; @@ -35,6 +36,8 @@ public class RacingControlComponent : ScriptedActivityComponent private MainWorldReturnData _returnData; + private int _activityId; + private uint _rankCounter = 0; public RacingControlComponent() @@ -55,6 +58,7 @@ private async Task LoadAsync() { case 1203: _deathPlaneHeight = 100; + _activityId = 42; _returnData = new MainWorldReturnData { ZoneId = 1200, @@ -64,6 +68,7 @@ private async Task LoadAsync() break; case 1303: _deathPlaneHeight = 0; + _activityId = 39; _returnData = new MainWorldReturnData { ZoneId = 1300, @@ -73,6 +78,7 @@ private async Task LoadAsync() break; case 1403: _deathPlaneHeight = 300; + _activityId = 45; _returnData = new MainWorldReturnData { ZoneId = 1400, @@ -493,7 +499,16 @@ private async Task PlayerReachedCheckpoint(Player player, int index) playerInfo.Lap++; Logger.Information($"{playerInfo.Player} now in lap {playerInfo.Lap}"); - + + // Set new best lap if applicable + if (lapTime < playerInfo.BestLapTime.Milliseconds) + { + playerInfo.BestLapTime = new TimeSpan(0, 0, 0, 0, lapTime); + + if (playerInfo.Player.TryGetComponent(out MissionInventoryComponent missionInventoryComponent)) + await missionInventoryComponent.RacingLaptimeAsync(lapTime); + } + // If player finished race if (playerInfo.Lap >= _raceInfo.LapCount) { @@ -507,7 +522,7 @@ private async Task PlayerReachedCheckpoint(Player player, int index) Logger.Information($"Race finished: {playerInfo.Player}, place {playerInfo.Finished}, time: {playerInfo.RaceTime.ElapsedMilliseconds}, smashed: {playerInfo.SmashedTimes}"); // TODO: Give rewards - // TODO: Update leaderboard + this.UpdateLeaderboard(playerInfo); if (_rankCounter >= _players.Count) { @@ -515,19 +530,91 @@ private async Task PlayerReachedCheckpoint(Player player, int index) // TODO: close instance } } + } - // Set new best lap if applicable - if (lapTime > playerInfo.BestLapTime.Milliseconds) + _players[playerInfoIndex] = playerInfo; + Logger.Information($"Player reached checkpoint: {index}"); + } + + private void UpdateLeaderboard(RacingPlayerInfo playerInfo) + { + var player = playerInfo.Player; + int lapTime = (int)(playerInfo.BestLapTime.TotalMilliseconds / 1000d); + int raceTime = (int)(playerInfo.RaceTime.ElapsedMilliseconds / 1000d); + var rank = playerInfo.Finished; + + Logger.Debug("RaceTime: " + raceTime + " LapTime: " + lapTime); + + var yearAndWeek = ISOWeek.GetYear(DateTime.Now) * 100 + ISOWeek.GetWeekOfYear(DateTime.Now); + using var ctx = new UchuContext(); + + var existingWeekly = ctx.ActivityScores.FirstOrDefault(entry => + entry.Activity == _activityId + && entry.Zone == Convert.ToInt32(player.Zone.ZoneId) + && entry.CharacterId == (long) player.Id + && entry.Week == yearAndWeek); + + var existingAllTime = ctx.ActivityScores.FirstOrDefault(entry => + entry.Activity == _activityId + && entry.Zone == Convert.ToInt32(player.Zone.ZoneId) + && entry.CharacterId == (long) player.Id + && entry.Week == 0); + + // Update existing weekly leaderboard entry + if (existingWeekly != null) + { + existingWeekly.Time = Math.Min(existingWeekly.Time, raceTime); + existingWeekly.BestLapTime = Math.Min(existingWeekly.BestLapTime, lapTime); + existingWeekly.Wins += rank == 1 ? 1 : 0; + + Logger.Debug("Weekly: " + existingWeekly.Time + " Lap: " + existingWeekly.BestLapTime); + ctx.ActivityScores.Update(existingWeekly); + } + // Add new entry + else + { + ctx.ActivityScores.Add(new ActivityScore { - playerInfo.BestLapTime = new TimeSpan(0, 0, 0, 0, lapTime); + Activity = _activityId, + Zone = player.Zone.ZoneId, + CharacterId = player.Id, + Time = raceTime, + BestLapTime = lapTime, + Wins = rank == 1 ? 1 : 0, + Week = yearAndWeek, + }); + } - if (playerInfo.Player.TryGetComponent(out MissionInventoryComponent missionInventoryComponent)) - await missionInventoryComponent.RacingLaptimeAsync(lapTime); - } + // Update existing all-time leaderboard entry. + if (existingAllTime != null) + { + existingAllTime.NumPlayed++; + existingAllTime.Time = Math.Min(existingAllTime.Time, raceTime); + existingAllTime.BestLapTime = Math.Min(existingAllTime.BestLapTime, lapTime); + existingAllTime.Wins += rank == 1 ? 1 : 0; + existingAllTime.LastPlayed = DateTimeOffset.Now.ToUnixTimeSeconds(); + + Logger.Debug("AllTime: " + existingAllTime.Time + " Lap: " + existingAllTime.BestLapTime); + ctx.ActivityScores.Update(existingAllTime); + } + // Add new entry. + else + { + ctx.ActivityScores.Add(new ActivityScore + { + Activity = _activityId, + Zone = player.Zone.ZoneId, + CharacterId = player.Id, + Time = raceTime, + BestLapTime = lapTime, + Wins = rank == 1 ? 1 : 0, + LastPlayed = DateTimeOffset.Now.ToUnixTimeSeconds(), + NumPlayed = 1, + Week = 0, + }); } - _players[playerInfoIndex] = playerInfo; - Logger.Information($"Player reached checkpoint: {index}"); + ctx.SaveChanges(); } public override void Construct(BitWriter writer) From ca86dd63785dd8e458b876dfcd46c4d45a384ad9 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Thu, 3 Feb 2022 21:12:42 +0100 Subject: [PATCH 48/71] Add migrations for racing leaderboards --- ...20203201112_RacingLeaderboards.Designer.cs | 836 +++++++++++++++++ .../20220203201112_RacingLeaderboards.cs | 37 + .../MySql/MySqlContextModelSnapshot.cs | 12 +- ...20203130407_RacingLeaderboards.Designer.cs | 867 ++++++++++++++++++ .../20220203130407_RacingLeaderboards.cs | 38 + .../Postgres/PostgresContextModelSnapshot.cs | 20 +- ...20203130149_RacingLeaderboards.Designer.cs | 834 +++++++++++++++++ .../20220203130149_RacingLeaderboards.cs | 37 + .../Sqlite/SqliteContextModelSnapshot.cs | 11 +- 9 files changed, 2683 insertions(+), 9 deletions(-) create mode 100644 Uchu.Core/Migrations/MySql/20220203201112_RacingLeaderboards.Designer.cs create mode 100644 Uchu.Core/Migrations/MySql/20220203201112_RacingLeaderboards.cs create mode 100644 Uchu.Core/Migrations/Postgres/20220203130407_RacingLeaderboards.Designer.cs create mode 100644 Uchu.Core/Migrations/Postgres/20220203130407_RacingLeaderboards.cs create mode 100644 Uchu.Core/Migrations/Sqlite/20220203130149_RacingLeaderboards.Designer.cs create mode 100644 Uchu.Core/Migrations/Sqlite/20220203130149_RacingLeaderboards.cs diff --git a/Uchu.Core/Migrations/MySql/20220203201112_RacingLeaderboards.Designer.cs b/Uchu.Core/Migrations/MySql/20220203201112_RacingLeaderboards.Designer.cs new file mode 100644 index 00000000..d5966985 --- /dev/null +++ b/Uchu.Core/Migrations/MySql/20220203201112_RacingLeaderboards.Designer.cs @@ -0,0 +1,836 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Uchu.Core.Providers; + +#nullable disable + +namespace Uchu.Core.Migrations.MySql +{ + [DbContext(typeof(MySqlContext))] + [Migration("20220203201112_RacingLeaderboards")] + partial class RacingLeaderboards + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Uchu.Core.ActivityScore", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("Activity") + .HasColumnType("int"); + + b.Property("BestLapTime") + .HasColumnType("int"); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("LastPlayed") + .HasColumnType("bigint"); + + b.Property("NumPlayed") + .HasColumnType("int"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("Time") + .HasColumnType("int"); + + b.Property("Week") + .HasColumnType("int"); + + b.Property("Wins") + .HasColumnType("int"); + + b.Property("Zone") + .HasColumnType("smallint unsigned"); + + b.HasKey("Id"); + + b.ToTable("ActivityScores"); + }); + + modelBuilder.Entity("Uchu.Core.Character", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("BaseHealth") + .HasColumnType("int"); + + b.Property("BaseImagination") + .HasColumnType("int"); + + b.Property("Currency") + .HasColumnType("bigint"); + + b.Property("CurrentArmor") + .HasColumnType("int"); + + b.Property("CurrentHealth") + .HasColumnType("int"); + + b.Property("CurrentImagination") + .HasColumnType("int"); + + b.Property("CustomName") + .IsRequired() + .HasMaxLength(33) + .HasColumnType("varchar(33)"); + + b.Property("EyeStyle") + .HasColumnType("bigint"); + + b.Property("EyebrowStyle") + .HasColumnType("bigint"); + + b.Property("FreeToPlay") + .HasColumnType("tinyint(1)"); + + b.Property("GuildId") + .HasColumnType("bigint"); + + b.Property("HairColor") + .HasColumnType("bigint"); + + b.Property("HairStyle") + .HasColumnType("bigint"); + + b.Property("InventorySize") + .HasColumnType("int"); + + b.Property("LandingByRocket") + .HasColumnType("tinyint(1)"); + + b.Property("LastActivity") + .HasColumnType("bigint"); + + b.Property("LastClone") + .HasColumnType("bigint"); + + b.Property("LastInstance") + .HasColumnType("int"); + + b.Property("LastZone") + .HasColumnType("int"); + + b.Property("LaunchedRocketFrom") + .HasColumnType("int"); + + b.Property("Level") + .HasColumnType("bigint"); + + b.Property("Lh") + .HasColumnType("bigint"); + + b.Property("MaximumArmor") + .HasColumnType("int"); + + b.Property("MaximumHealth") + .HasColumnType("int"); + + b.Property("MaximumImagination") + .HasColumnType("int"); + + b.Property("MouthStyle") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(33) + .HasColumnType("varchar(33)"); + + b.Property("NameRejected") + .HasColumnType("tinyint(1)"); + + b.Property("PantsColor") + .HasColumnType("bigint"); + + b.Property("Rh") + .HasColumnType("bigint"); + + b.Property("Rocket") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("ShirtColor") + .HasColumnType("bigint"); + + b.Property("ShirtStyle") + .HasColumnType("bigint"); + + b.Property("SpawnLocationName") + .HasMaxLength(33) + .HasColumnType("varchar(33)"); + + b.Property("SpawnPositionX") + .HasColumnType("float"); + + b.Property("SpawnPositionY") + .HasColumnType("float"); + + b.Property("SpawnPositionZ") + .HasColumnType("float"); + + b.Property("SpawnRotationW") + .HasColumnType("float"); + + b.Property("SpawnRotationX") + .HasColumnType("float"); + + b.Property("SpawnRotationY") + .HasColumnType("float"); + + b.Property("SpawnRotationZ") + .HasColumnType("float"); + + b.Property("TotalArmorPowerUpsCollected") + .HasColumnType("bigint"); + + b.Property("TotalArmorRepaired") + .HasColumnType("bigint"); + + b.Property("TotalBricksCollected") + .HasColumnType("bigint"); + + b.Property("TotalCurrencyCollected") + .HasColumnType("bigint"); + + b.Property("TotalDamageHealed") + .HasColumnType("bigint"); + + b.Property("TotalDamageTaken") + .HasColumnType("bigint"); + + b.Property("TotalDistanceDriven") + .HasColumnType("bigint"); + + b.Property("TotalDistanceTraveled") + .HasColumnType("bigint"); + + b.Property("TotalEnemiesSmashed") + .HasColumnType("bigint"); + + b.Property("TotalFirstPlaceFinishes") + .HasColumnType("bigint"); + + b.Property("TotalImaginationPowerUpsCollected") + .HasColumnType("bigint"); + + b.Property("TotalImaginationRestored") + .HasColumnType("bigint"); + + b.Property("TotalImaginationUsed") + .HasColumnType("bigint"); + + b.Property("TotalLifePowerUpsCollected") + .HasColumnType("bigint"); + + b.Property("TotalMissionsCompleted") + .HasColumnType("bigint"); + + b.Property("TotalPetsTamed") + .HasColumnType("bigint"); + + b.Property("TotalQuickBuildsCompleted") + .HasColumnType("bigint"); + + b.Property("TotalRacecarBoostsActivated") + .HasColumnType("bigint"); + + b.Property("TotalRacecarWrecks") + .HasColumnType("bigint"); + + b.Property("TotalRacesFinished") + .HasColumnType("bigint"); + + b.Property("TotalRacingImaginationCratesSmashed") + .HasColumnType("bigint"); + + b.Property("TotalRacingImaginationPowerUpsCollected") + .HasColumnType("bigint"); + + b.Property("TotalRacingSmashablesSmashed") + .HasColumnType("bigint"); + + b.Property("TotalRocketsUsed") + .HasColumnType("bigint"); + + b.Property("TotalSmashablesSmashed") + .HasColumnType("bigint"); + + b.Property("TotalSuicides") + .HasColumnType("bigint"); + + b.Property("TotalTimeAirborne") + .HasColumnType("bigint"); + + b.Property("UniverseScore") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("VaultInventorySize") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Characters"); + }); + + modelBuilder.Entity("Uchu.Core.CharacterFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("Flag") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.ToTable("Flags"); + }); + + modelBuilder.Entity("Uchu.Core.CharacterMail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("AttachmentCount") + .HasColumnType("smallint unsigned"); + + b.Property("AttachmentCurrency") + .HasColumnType("bigint unsigned"); + + b.Property("AttachmentLot") + .HasColumnType("int"); + + b.Property("AuthorId") + .HasColumnType("bigint"); + + b.Property("Body") + .HasColumnType("longtext"); + + b.Property("ExpirationTime") + .HasColumnType("datetime(6)"); + + b.Property("Read") + .HasColumnType("tinyint(1)"); + + b.Property("RecipientId") + .HasColumnType("bigint"); + + b.Property("SentTime") + .HasColumnType("datetime(6)"); + + b.Property("Subject") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Mails"); + }); + + modelBuilder.Entity("Uchu.Core.CharacterTrade", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CurrencyA") + .HasColumnType("bigint"); + + b.Property("CurrencyB") + .HasColumnType("bigint"); + + b.Property("PartyA") + .HasColumnType("bigint"); + + b.Property("PartyB") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("Trades"); + }); + + modelBuilder.Entity("Uchu.Core.ChatTranscript", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("Author") + .HasColumnType("bigint"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("Receiver") + .HasColumnType("bigint"); + + b.Property("SentTime") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("ChatTranscript"); + }); + + modelBuilder.Entity("Uchu.Core.Friend", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("BestFriend") + .HasColumnType("tinyint(1)"); + + b.Property("FriendA") + .HasColumnType("bigint"); + + b.Property("FriendB") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("Friends"); + }); + + modelBuilder.Entity("Uchu.Core.FriendRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("BestFriend") + .HasColumnType("tinyint(1)"); + + b.Property("Receiver") + .HasColumnType("bigint"); + + b.Property("Sender") + .HasColumnType("bigint"); + + b.Property("Sent") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("FriendRequests"); + }); + + modelBuilder.Entity("Uchu.Core.Guild", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("CreatorId") + .HasColumnType("bigint"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Guilds"); + }); + + modelBuilder.Entity("Uchu.Core.GuildInvite", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("GuildId") + .HasColumnType("bigint"); + + b.Property("RecipientId") + .HasColumnType("bigint"); + + b.Property("SenderId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("GuildId"); + + b.ToTable("GuildInvites"); + }); + + modelBuilder.Entity("Uchu.Core.InventoryItem", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("Count") + .HasColumnType("bigint"); + + b.Property("ExtraInfo") + .HasColumnType("longtext"); + + b.Property("InventoryType") + .HasColumnType("int"); + + b.Property("IsBound") + .HasColumnType("tinyint(1)"); + + b.Property("IsEquipped") + .HasColumnType("tinyint(1)"); + + b.Property("Lot") + .HasColumnType("int"); + + b.Property("ParentId") + .HasColumnType("bigint"); + + b.Property("Slot") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.ToTable("InventoryItems"); + }); + + modelBuilder.Entity("Uchu.Core.Mission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("CompletionCount") + .HasColumnType("int"); + + b.Property("LastCompletion") + .HasColumnType("bigint"); + + b.Property("MissionId") + .HasColumnType("int"); + + b.Property("State") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.ToTable("Missions"); + }); + + modelBuilder.Entity("Uchu.Core.MissionTask", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("MissionId") + .HasColumnType("int"); + + b.Property("TaskId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("MissionId"); + + b.ToTable("MissionTasks"); + }); + + modelBuilder.Entity("Uchu.Core.MissionTaskValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("Count") + .HasColumnType("int"); + + b.Property("MissionTaskId") + .HasColumnType("int"); + + b.Property("Value") + .HasColumnType("float"); + + b.HasKey("Id"); + + b.HasIndex("MissionTaskId"); + + b.ToTable("MissionTaskValue"); + }); + + modelBuilder.Entity("Uchu.Core.SessionCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("ZoneId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("SessionCaches"); + }); + + modelBuilder.Entity("Uchu.Core.TradeTransactionItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ItemId") + .HasColumnType("bigint"); + + b.Property("Party") + .HasColumnType("bigint"); + + b.Property("TradeId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("TradeId"); + + b.ToTable("TransactionItems"); + }); + + modelBuilder.Entity("Uchu.Core.UnlockedEmote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("EmoteId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.ToTable("UnlockedEmote"); + }); + + modelBuilder.Entity("Uchu.Core.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("Banned") + .HasColumnType("tinyint(1)"); + + b.Property("BannedReason") + .HasColumnType("longtext"); + + b.Property("CharacterIndex") + .HasColumnType("int"); + + b.Property("CustomLockout") + .HasColumnType("longtext"); + + b.Property("FirstTimeOnSubscription") + .HasColumnType("tinyint(1)"); + + b.Property("FreeToPlay") + .HasColumnType("tinyint(1)"); + + b.Property("GameMasterLevel") + .HasColumnType("int"); + + b.Property("Password") + .HasMaxLength(60) + .HasColumnType("varchar(60)"); + + b.Property("Sso") + .HasColumnType("tinyint(1)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(33) + .HasColumnType("varchar(33)"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Uchu.Core.Character", b => + { + b.HasOne("Uchu.Core.User", "User") + .WithMany("Characters") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Uchu.Core.CharacterFlag", b => + { + b.HasOne("Uchu.Core.Character", "Character") + .WithMany("Flags") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("Uchu.Core.GuildInvite", b => + { + b.HasOne("Uchu.Core.Guild", "Guild") + .WithMany("Invites") + .HasForeignKey("GuildId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Guild"); + }); + + modelBuilder.Entity("Uchu.Core.InventoryItem", b => + { + b.HasOne("Uchu.Core.Character", "Character") + .WithMany("Items") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("Uchu.Core.Mission", b => + { + b.HasOne("Uchu.Core.Character", "Character") + .WithMany("Missions") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("Uchu.Core.MissionTask", b => + { + b.HasOne("Uchu.Core.Mission", "Mission") + .WithMany("Tasks") + .HasForeignKey("MissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Mission"); + }); + + modelBuilder.Entity("Uchu.Core.MissionTaskValue", b => + { + b.HasOne("Uchu.Core.MissionTask", "MissionTask") + .WithMany("Values") + .HasForeignKey("MissionTaskId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MissionTask"); + }); + + modelBuilder.Entity("Uchu.Core.TradeTransactionItem", b => + { + b.HasOne("Uchu.Core.CharacterTrade", "Trade") + .WithMany("TransactionItems") + .HasForeignKey("TradeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Trade"); + }); + + modelBuilder.Entity("Uchu.Core.UnlockedEmote", b => + { + b.HasOne("Uchu.Core.Character", "Character") + .WithMany("UnlockedEmotes") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("Uchu.Core.Character", b => + { + b.Navigation("Flags"); + + b.Navigation("Items"); + + b.Navigation("Missions"); + + b.Navigation("UnlockedEmotes"); + }); + + modelBuilder.Entity("Uchu.Core.CharacterTrade", b => + { + b.Navigation("TransactionItems"); + }); + + modelBuilder.Entity("Uchu.Core.Guild", b => + { + b.Navigation("Invites"); + }); + + modelBuilder.Entity("Uchu.Core.Mission", b => + { + b.Navigation("Tasks"); + }); + + modelBuilder.Entity("Uchu.Core.MissionTask", b => + { + b.Navigation("Values"); + }); + + modelBuilder.Entity("Uchu.Core.User", b => + { + b.Navigation("Characters"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Uchu.Core/Migrations/MySql/20220203201112_RacingLeaderboards.cs b/Uchu.Core/Migrations/MySql/20220203201112_RacingLeaderboards.cs new file mode 100644 index 00000000..caf5892f --- /dev/null +++ b/Uchu.Core/Migrations/MySql/20220203201112_RacingLeaderboards.cs @@ -0,0 +1,37 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Uchu.Core.Migrations.MySql +{ + public partial class RacingLeaderboards : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "BestLapTime", + table: "ActivityScores", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "Wins", + table: "ActivityScores", + type: "int", + nullable: false, + defaultValue: 0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "BestLapTime", + table: "ActivityScores"); + + migrationBuilder.DropColumn( + name: "Wins", + table: "ActivityScores"); + } + } +} diff --git a/Uchu.Core/Migrations/MySql/MySqlContextModelSnapshot.cs b/Uchu.Core/Migrations/MySql/MySqlContextModelSnapshot.cs index c9d7f787..8e6e137d 100644 --- a/Uchu.Core/Migrations/MySql/MySqlContextModelSnapshot.cs +++ b/Uchu.Core/Migrations/MySql/MySqlContextModelSnapshot.cs @@ -5,6 +5,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Uchu.Core.Providers; +#nullable disable + namespace Uchu.Core.Migrations.MySql { [DbContext(typeof(MySqlContext))] @@ -14,8 +16,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("Relational:MaxIdentifierLength", 64) - .HasAnnotation("ProductVersion", "5.0.5"); + .HasAnnotation("ProductVersion", "6.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 64); modelBuilder.Entity("Uchu.Core.ActivityScore", b => { @@ -26,6 +28,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Activity") .HasColumnType("int"); + b.Property("BestLapTime") + .HasColumnType("int"); + b.Property("CharacterId") .HasColumnType("bigint"); @@ -44,6 +49,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Week") .HasColumnType("int"); + b.Property("Wins") + .HasColumnType("int"); + b.Property("Zone") .HasColumnType("smallint unsigned"); diff --git a/Uchu.Core/Migrations/Postgres/20220203130407_RacingLeaderboards.Designer.cs b/Uchu.Core/Migrations/Postgres/20220203130407_RacingLeaderboards.Designer.cs new file mode 100644 index 00000000..59c7a957 --- /dev/null +++ b/Uchu.Core/Migrations/Postgres/20220203130407_RacingLeaderboards.Designer.cs @@ -0,0 +1,867 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Uchu.Core.Providers; + +#nullable disable + +namespace Uchu.Core.Migrations +{ + [DbContext(typeof(PostgresContext))] + [Migration("20220203130407_RacingLeaderboards")] + partial class RacingLeaderboards + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Uchu.Core.ActivityScore", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Activity") + .HasColumnType("integer"); + + b.Property("BestLapTime") + .HasColumnType("integer"); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("LastPlayed") + .HasColumnType("bigint"); + + b.Property("NumPlayed") + .HasColumnType("integer"); + + b.Property("Points") + .HasColumnType("integer"); + + b.Property("Time") + .HasColumnType("integer"); + + b.Property("Week") + .HasColumnType("integer"); + + b.Property("Wins") + .HasColumnType("integer"); + + b.Property("Zone") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("ActivityScores"); + }); + + modelBuilder.Entity("Uchu.Core.Character", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("BaseHealth") + .HasColumnType("integer"); + + b.Property("BaseImagination") + .HasColumnType("integer"); + + b.Property("Currency") + .HasColumnType("bigint"); + + b.Property("CurrentArmor") + .HasColumnType("integer"); + + b.Property("CurrentHealth") + .HasColumnType("integer"); + + b.Property("CurrentImagination") + .HasColumnType("integer"); + + b.Property("CustomName") + .IsRequired() + .HasMaxLength(33) + .HasColumnType("character varying(33)"); + + b.Property("EyeStyle") + .HasColumnType("bigint"); + + b.Property("EyebrowStyle") + .HasColumnType("bigint"); + + b.Property("FreeToPlay") + .HasColumnType("boolean"); + + b.Property("GuildId") + .HasColumnType("bigint"); + + b.Property("HairColor") + .HasColumnType("bigint"); + + b.Property("HairStyle") + .HasColumnType("bigint"); + + b.Property("InventorySize") + .HasColumnType("integer"); + + b.Property("LandingByRocket") + .HasColumnType("boolean"); + + b.Property("LastActivity") + .HasColumnType("bigint"); + + b.Property("LastClone") + .HasColumnType("bigint"); + + b.Property("LastInstance") + .HasColumnType("integer"); + + b.Property("LastZone") + .HasColumnType("integer"); + + b.Property("LaunchedRocketFrom") + .HasColumnType("integer"); + + b.Property("Level") + .HasColumnType("bigint"); + + b.Property("Lh") + .HasColumnType("bigint"); + + b.Property("MaximumArmor") + .HasColumnType("integer"); + + b.Property("MaximumHealth") + .HasColumnType("integer"); + + b.Property("MaximumImagination") + .HasColumnType("integer"); + + b.Property("MouthStyle") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(33) + .HasColumnType("character varying(33)"); + + b.Property("NameRejected") + .HasColumnType("boolean"); + + b.Property("PantsColor") + .HasColumnType("bigint"); + + b.Property("Rh") + .HasColumnType("bigint"); + + b.Property("Rocket") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("ShirtColor") + .HasColumnType("bigint"); + + b.Property("ShirtStyle") + .HasColumnType("bigint"); + + b.Property("SpawnLocationName") + .HasMaxLength(33) + .HasColumnType("character varying(33)"); + + b.Property("SpawnPositionX") + .HasColumnType("real"); + + b.Property("SpawnPositionY") + .HasColumnType("real"); + + b.Property("SpawnPositionZ") + .HasColumnType("real"); + + b.Property("SpawnRotationW") + .HasColumnType("real"); + + b.Property("SpawnRotationX") + .HasColumnType("real"); + + b.Property("SpawnRotationY") + .HasColumnType("real"); + + b.Property("SpawnRotationZ") + .HasColumnType("real"); + + b.Property("TotalArmorPowerUpsCollected") + .HasColumnType("bigint"); + + b.Property("TotalArmorRepaired") + .HasColumnType("bigint"); + + b.Property("TotalBricksCollected") + .HasColumnType("bigint"); + + b.Property("TotalCurrencyCollected") + .HasColumnType("bigint"); + + b.Property("TotalDamageHealed") + .HasColumnType("bigint"); + + b.Property("TotalDamageTaken") + .HasColumnType("bigint"); + + b.Property("TotalDistanceDriven") + .HasColumnType("bigint"); + + b.Property("TotalDistanceTraveled") + .HasColumnType("bigint"); + + b.Property("TotalEnemiesSmashed") + .HasColumnType("bigint"); + + b.Property("TotalFirstPlaceFinishes") + .HasColumnType("bigint"); + + b.Property("TotalImaginationPowerUpsCollected") + .HasColumnType("bigint"); + + b.Property("TotalImaginationRestored") + .HasColumnType("bigint"); + + b.Property("TotalImaginationUsed") + .HasColumnType("bigint"); + + b.Property("TotalLifePowerUpsCollected") + .HasColumnType("bigint"); + + b.Property("TotalMissionsCompleted") + .HasColumnType("bigint"); + + b.Property("TotalPetsTamed") + .HasColumnType("bigint"); + + b.Property("TotalQuickBuildsCompleted") + .HasColumnType("bigint"); + + b.Property("TotalRacecarBoostsActivated") + .HasColumnType("bigint"); + + b.Property("TotalRacecarWrecks") + .HasColumnType("bigint"); + + b.Property("TotalRacesFinished") + .HasColumnType("bigint"); + + b.Property("TotalRacingImaginationCratesSmashed") + .HasColumnType("bigint"); + + b.Property("TotalRacingImaginationPowerUpsCollected") + .HasColumnType("bigint"); + + b.Property("TotalRacingSmashablesSmashed") + .HasColumnType("bigint"); + + b.Property("TotalRocketsUsed") + .HasColumnType("bigint"); + + b.Property("TotalSmashablesSmashed") + .HasColumnType("bigint"); + + b.Property("TotalSuicides") + .HasColumnType("bigint"); + + b.Property("TotalTimeAirborne") + .HasColumnType("bigint"); + + b.Property("UniverseScore") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("VaultInventorySize") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Characters"); + }); + + modelBuilder.Entity("Uchu.Core.CharacterFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("Flag") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.ToTable("Flags"); + }); + + modelBuilder.Entity("Uchu.Core.CharacterMail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AttachmentCount") + .HasColumnType("integer"); + + b.Property("AttachmentCurrency") + .HasColumnType("numeric(20,0)"); + + b.Property("AttachmentLot") + .HasColumnType("integer"); + + b.Property("AuthorId") + .HasColumnType("bigint"); + + b.Property("Body") + .HasColumnType("text"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Read") + .HasColumnType("boolean"); + + b.Property("RecipientId") + .HasColumnType("bigint"); + + b.Property("SentTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Subject") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Mails"); + }); + + modelBuilder.Entity("Uchu.Core.CharacterTrade", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CurrencyA") + .HasColumnType("bigint"); + + b.Property("CurrencyB") + .HasColumnType("bigint"); + + b.Property("PartyA") + .HasColumnType("bigint"); + + b.Property("PartyB") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("Trades"); + }); + + modelBuilder.Entity("Uchu.Core.ChatTranscript", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Author") + .HasColumnType("bigint"); + + b.Property("Message") + .HasColumnType("text"); + + b.Property("Receiver") + .HasColumnType("bigint"); + + b.Property("SentTime") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("ChatTranscript"); + }); + + modelBuilder.Entity("Uchu.Core.Friend", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BestFriend") + .HasColumnType("boolean"); + + b.Property("FriendA") + .HasColumnType("bigint"); + + b.Property("FriendB") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("Friends"); + }); + + modelBuilder.Entity("Uchu.Core.FriendRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BestFriend") + .HasColumnType("boolean"); + + b.Property("Receiver") + .HasColumnType("bigint"); + + b.Property("Sender") + .HasColumnType("bigint"); + + b.Property("Sent") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("FriendRequests"); + }); + + modelBuilder.Entity("Uchu.Core.Guild", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("CreatorId") + .HasColumnType("bigint"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Guilds"); + }); + + modelBuilder.Entity("Uchu.Core.GuildInvite", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("GuildId") + .HasColumnType("bigint"); + + b.Property("RecipientId") + .HasColumnType("bigint"); + + b.Property("SenderId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("GuildId"); + + b.ToTable("GuildInvites"); + }); + + modelBuilder.Entity("Uchu.Core.InventoryItem", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("Count") + .HasColumnType("bigint"); + + b.Property("ExtraInfo") + .HasColumnType("text"); + + b.Property("InventoryType") + .HasColumnType("integer"); + + b.Property("IsBound") + .HasColumnType("boolean"); + + b.Property("IsEquipped") + .HasColumnType("boolean"); + + b.Property("Lot") + .HasColumnType("integer"); + + b.Property("ParentId") + .HasColumnType("bigint"); + + b.Property("Slot") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.ToTable("InventoryItems"); + }); + + modelBuilder.Entity("Uchu.Core.Mission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("CompletionCount") + .HasColumnType("integer"); + + b.Property("LastCompletion") + .HasColumnType("bigint"); + + b.Property("MissionId") + .HasColumnType("integer"); + + b.Property("State") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.ToTable("Missions"); + }); + + modelBuilder.Entity("Uchu.Core.MissionTask", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("MissionId") + .HasColumnType("integer"); + + b.Property("TaskId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MissionId"); + + b.ToTable("MissionTasks"); + }); + + modelBuilder.Entity("Uchu.Core.MissionTaskValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Count") + .HasColumnType("integer"); + + b.Property("MissionTaskId") + .HasColumnType("integer"); + + b.Property("Value") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("MissionTaskId"); + + b.ToTable("MissionTaskValue"); + }); + + modelBuilder.Entity("Uchu.Core.SessionCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("ZoneId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("SessionCaches"); + }); + + modelBuilder.Entity("Uchu.Core.TradeTransactionItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ItemId") + .HasColumnType("bigint"); + + b.Property("Party") + .HasColumnType("bigint"); + + b.Property("TradeId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("TradeId"); + + b.ToTable("TransactionItems"); + }); + + modelBuilder.Entity("Uchu.Core.UnlockedEmote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("EmoteId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.ToTable("UnlockedEmote"); + }); + + modelBuilder.Entity("Uchu.Core.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Banned") + .HasColumnType("boolean"); + + b.Property("BannedReason") + .HasColumnType("text"); + + b.Property("CharacterIndex") + .HasColumnType("integer"); + + b.Property("CustomLockout") + .HasColumnType("text"); + + b.Property("FirstTimeOnSubscription") + .HasColumnType("boolean"); + + b.Property("FreeToPlay") + .HasColumnType("boolean"); + + b.Property("GameMasterLevel") + .HasColumnType("integer"); + + b.Property("Password") + .HasMaxLength(60) + .HasColumnType("character varying(60)"); + + b.Property("Sso") + .HasColumnType("boolean"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(33) + .HasColumnType("character varying(33)"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Uchu.Core.Character", b => + { + b.HasOne("Uchu.Core.User", "User") + .WithMany("Characters") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Uchu.Core.CharacterFlag", b => + { + b.HasOne("Uchu.Core.Character", "Character") + .WithMany("Flags") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("Uchu.Core.GuildInvite", b => + { + b.HasOne("Uchu.Core.Guild", "Guild") + .WithMany("Invites") + .HasForeignKey("GuildId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Guild"); + }); + + modelBuilder.Entity("Uchu.Core.InventoryItem", b => + { + b.HasOne("Uchu.Core.Character", "Character") + .WithMany("Items") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("Uchu.Core.Mission", b => + { + b.HasOne("Uchu.Core.Character", "Character") + .WithMany("Missions") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("Uchu.Core.MissionTask", b => + { + b.HasOne("Uchu.Core.Mission", "Mission") + .WithMany("Tasks") + .HasForeignKey("MissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Mission"); + }); + + modelBuilder.Entity("Uchu.Core.MissionTaskValue", b => + { + b.HasOne("Uchu.Core.MissionTask", "MissionTask") + .WithMany("Values") + .HasForeignKey("MissionTaskId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MissionTask"); + }); + + modelBuilder.Entity("Uchu.Core.TradeTransactionItem", b => + { + b.HasOne("Uchu.Core.CharacterTrade", "Trade") + .WithMany("TransactionItems") + .HasForeignKey("TradeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Trade"); + }); + + modelBuilder.Entity("Uchu.Core.UnlockedEmote", b => + { + b.HasOne("Uchu.Core.Character", "Character") + .WithMany("UnlockedEmotes") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("Uchu.Core.Character", b => + { + b.Navigation("Flags"); + + b.Navigation("Items"); + + b.Navigation("Missions"); + + b.Navigation("UnlockedEmotes"); + }); + + modelBuilder.Entity("Uchu.Core.CharacterTrade", b => + { + b.Navigation("TransactionItems"); + }); + + modelBuilder.Entity("Uchu.Core.Guild", b => + { + b.Navigation("Invites"); + }); + + modelBuilder.Entity("Uchu.Core.Mission", b => + { + b.Navigation("Tasks"); + }); + + modelBuilder.Entity("Uchu.Core.MissionTask", b => + { + b.Navigation("Values"); + }); + + modelBuilder.Entity("Uchu.Core.User", b => + { + b.Navigation("Characters"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Uchu.Core/Migrations/Postgres/20220203130407_RacingLeaderboards.cs b/Uchu.Core/Migrations/Postgres/20220203130407_RacingLeaderboards.cs new file mode 100644 index 00000000..280cc423 --- /dev/null +++ b/Uchu.Core/Migrations/Postgres/20220203130407_RacingLeaderboards.cs @@ -0,0 +1,38 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Uchu.Core.Migrations +{ + public partial class RacingLeaderboards : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "BestLapTime", + table: "ActivityScores", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "Wins", + table: "ActivityScores", + type: "integer", + nullable: false, + defaultValue: 0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "BestLapTime", + table: "ActivityScores"); + + migrationBuilder.DropColumn( + name: "Wins", + table: "ActivityScores"); + } + } +} diff --git a/Uchu.Core/Migrations/Postgres/PostgresContextModelSnapshot.cs b/Uchu.Core/Migrations/Postgres/PostgresContextModelSnapshot.cs index 8a8cbcd3..e55162a0 100644 --- a/Uchu.Core/Migrations/Postgres/PostgresContextModelSnapshot.cs +++ b/Uchu.Core/Migrations/Postgres/PostgresContextModelSnapshot.cs @@ -6,6 +6,8 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using Uchu.Core.Providers; +#nullable disable + namespace Uchu.Core.Migrations { [DbContext(typeof(PostgresContext))] @@ -15,20 +17,25 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("Relational:MaxIdentifierLength", 63) - .HasAnnotation("ProductVersion", "5.0.5") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasAnnotation("ProductVersion", "6.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("Uchu.Core.ActivityScore", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); b.Property("Activity") .HasColumnType("integer"); + b.Property("BestLapTime") + .HasColumnType("integer"); + b.Property("CharacterId") .HasColumnType("bigint"); @@ -47,6 +54,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Week") .HasColumnType("integer"); + b.Property("Wins") + .HasColumnType("integer"); + b.Property("Zone") .HasColumnType("integer"); diff --git a/Uchu.Core/Migrations/Sqlite/20220203130149_RacingLeaderboards.Designer.cs b/Uchu.Core/Migrations/Sqlite/20220203130149_RacingLeaderboards.Designer.cs new file mode 100644 index 00000000..5f052d10 --- /dev/null +++ b/Uchu.Core/Migrations/Sqlite/20220203130149_RacingLeaderboards.Designer.cs @@ -0,0 +1,834 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Uchu.Core.Providers; + +#nullable disable + +namespace Uchu.Core.Migrations.Sqlite +{ + [DbContext(typeof(SqliteContext))] + [Migration("20220203130149_RacingLeaderboards")] + partial class RacingLeaderboards + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.0"); + + modelBuilder.Entity("Uchu.Core.ActivityScore", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Activity") + .HasColumnType("INTEGER"); + + b.Property("BestLapTime") + .HasColumnType("INTEGER"); + + b.Property("CharacterId") + .HasColumnType("INTEGER"); + + b.Property("LastPlayed") + .HasColumnType("INTEGER"); + + b.Property("NumPlayed") + .HasColumnType("INTEGER"); + + b.Property("Points") + .HasColumnType("INTEGER"); + + b.Property("Time") + .HasColumnType("INTEGER"); + + b.Property("Week") + .HasColumnType("INTEGER"); + + b.Property("Wins") + .HasColumnType("INTEGER"); + + b.Property("Zone") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("ActivityScores"); + }); + + modelBuilder.Entity("Uchu.Core.Character", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("BaseHealth") + .HasColumnType("INTEGER"); + + b.Property("BaseImagination") + .HasColumnType("INTEGER"); + + b.Property("Currency") + .HasColumnType("INTEGER"); + + b.Property("CurrentArmor") + .HasColumnType("INTEGER"); + + b.Property("CurrentHealth") + .HasColumnType("INTEGER"); + + b.Property("CurrentImagination") + .HasColumnType("INTEGER"); + + b.Property("CustomName") + .IsRequired() + .HasMaxLength(33) + .HasColumnType("TEXT"); + + b.Property("EyeStyle") + .HasColumnType("INTEGER"); + + b.Property("EyebrowStyle") + .HasColumnType("INTEGER"); + + b.Property("FreeToPlay") + .HasColumnType("INTEGER"); + + b.Property("GuildId") + .HasColumnType("INTEGER"); + + b.Property("HairColor") + .HasColumnType("INTEGER"); + + b.Property("HairStyle") + .HasColumnType("INTEGER"); + + b.Property("InventorySize") + .HasColumnType("INTEGER"); + + b.Property("LandingByRocket") + .HasColumnType("INTEGER"); + + b.Property("LastActivity") + .HasColumnType("INTEGER"); + + b.Property("LastClone") + .HasColumnType("INTEGER"); + + b.Property("LastInstance") + .HasColumnType("INTEGER"); + + b.Property("LastZone") + .HasColumnType("INTEGER"); + + b.Property("LaunchedRocketFrom") + .HasColumnType("INTEGER"); + + b.Property("Level") + .HasColumnType("INTEGER"); + + b.Property("Lh") + .HasColumnType("INTEGER"); + + b.Property("MaximumArmor") + .HasColumnType("INTEGER"); + + b.Property("MaximumHealth") + .HasColumnType("INTEGER"); + + b.Property("MaximumImagination") + .HasColumnType("INTEGER"); + + b.Property("MouthStyle") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(33) + .HasColumnType("TEXT"); + + b.Property("NameRejected") + .HasColumnType("INTEGER"); + + b.Property("PantsColor") + .HasColumnType("INTEGER"); + + b.Property("Rh") + .HasColumnType("INTEGER"); + + b.Property("Rocket") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("ShirtColor") + .HasColumnType("INTEGER"); + + b.Property("ShirtStyle") + .HasColumnType("INTEGER"); + + b.Property("SpawnLocationName") + .HasMaxLength(33) + .HasColumnType("TEXT"); + + b.Property("SpawnPositionX") + .HasColumnType("REAL"); + + b.Property("SpawnPositionY") + .HasColumnType("REAL"); + + b.Property("SpawnPositionZ") + .HasColumnType("REAL"); + + b.Property("SpawnRotationW") + .HasColumnType("REAL"); + + b.Property("SpawnRotationX") + .HasColumnType("REAL"); + + b.Property("SpawnRotationY") + .HasColumnType("REAL"); + + b.Property("SpawnRotationZ") + .HasColumnType("REAL"); + + b.Property("TotalArmorPowerUpsCollected") + .HasColumnType("INTEGER"); + + b.Property("TotalArmorRepaired") + .HasColumnType("INTEGER"); + + b.Property("TotalBricksCollected") + .HasColumnType("INTEGER"); + + b.Property("TotalCurrencyCollected") + .HasColumnType("INTEGER"); + + b.Property("TotalDamageHealed") + .HasColumnType("INTEGER"); + + b.Property("TotalDamageTaken") + .HasColumnType("INTEGER"); + + b.Property("TotalDistanceDriven") + .HasColumnType("INTEGER"); + + b.Property("TotalDistanceTraveled") + .HasColumnType("INTEGER"); + + b.Property("TotalEnemiesSmashed") + .HasColumnType("INTEGER"); + + b.Property("TotalFirstPlaceFinishes") + .HasColumnType("INTEGER"); + + b.Property("TotalImaginationPowerUpsCollected") + .HasColumnType("INTEGER"); + + b.Property("TotalImaginationRestored") + .HasColumnType("INTEGER"); + + b.Property("TotalImaginationUsed") + .HasColumnType("INTEGER"); + + b.Property("TotalLifePowerUpsCollected") + .HasColumnType("INTEGER"); + + b.Property("TotalMissionsCompleted") + .HasColumnType("INTEGER"); + + b.Property("TotalPetsTamed") + .HasColumnType("INTEGER"); + + b.Property("TotalQuickBuildsCompleted") + .HasColumnType("INTEGER"); + + b.Property("TotalRacecarBoostsActivated") + .HasColumnType("INTEGER"); + + b.Property("TotalRacecarWrecks") + .HasColumnType("INTEGER"); + + b.Property("TotalRacesFinished") + .HasColumnType("INTEGER"); + + b.Property("TotalRacingImaginationCratesSmashed") + .HasColumnType("INTEGER"); + + b.Property("TotalRacingImaginationPowerUpsCollected") + .HasColumnType("INTEGER"); + + b.Property("TotalRacingSmashablesSmashed") + .HasColumnType("INTEGER"); + + b.Property("TotalRocketsUsed") + .HasColumnType("INTEGER"); + + b.Property("TotalSmashablesSmashed") + .HasColumnType("INTEGER"); + + b.Property("TotalSuicides") + .HasColumnType("INTEGER"); + + b.Property("TotalTimeAirborne") + .HasColumnType("INTEGER"); + + b.Property("UniverseScore") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.Property("VaultInventorySize") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Characters"); + }); + + modelBuilder.Entity("Uchu.Core.CharacterFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CharacterId") + .HasColumnType("INTEGER"); + + b.Property("Flag") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.ToTable("Flags"); + }); + + modelBuilder.Entity("Uchu.Core.CharacterMail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AttachmentCount") + .HasColumnType("INTEGER"); + + b.Property("AttachmentCurrency") + .HasColumnType("INTEGER"); + + b.Property("AttachmentLot") + .HasColumnType("INTEGER"); + + b.Property("AuthorId") + .HasColumnType("INTEGER"); + + b.Property("Body") + .HasColumnType("TEXT"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT"); + + b.Property("Read") + .HasColumnType("INTEGER"); + + b.Property("RecipientId") + .HasColumnType("INTEGER"); + + b.Property("SentTime") + .HasColumnType("TEXT"); + + b.Property("Subject") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Mails"); + }); + + modelBuilder.Entity("Uchu.Core.CharacterTrade", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CurrencyA") + .HasColumnType("INTEGER"); + + b.Property("CurrencyB") + .HasColumnType("INTEGER"); + + b.Property("PartyA") + .HasColumnType("INTEGER"); + + b.Property("PartyB") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Trades"); + }); + + modelBuilder.Entity("Uchu.Core.ChatTranscript", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Author") + .HasColumnType("INTEGER"); + + b.Property("Message") + .HasColumnType("TEXT"); + + b.Property("Receiver") + .HasColumnType("INTEGER"); + + b.Property("SentTime") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChatTranscript"); + }); + + modelBuilder.Entity("Uchu.Core.Friend", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BestFriend") + .HasColumnType("INTEGER"); + + b.Property("FriendA") + .HasColumnType("INTEGER"); + + b.Property("FriendB") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Friends"); + }); + + modelBuilder.Entity("Uchu.Core.FriendRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BestFriend") + .HasColumnType("INTEGER"); + + b.Property("Receiver") + .HasColumnType("INTEGER"); + + b.Property("Sender") + .HasColumnType("INTEGER"); + + b.Property("Sent") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("FriendRequests"); + }); + + modelBuilder.Entity("Uchu.Core.Guild", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("CreatorId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Guilds"); + }); + + modelBuilder.Entity("Uchu.Core.GuildInvite", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("GuildId") + .HasColumnType("INTEGER"); + + b.Property("RecipientId") + .HasColumnType("INTEGER"); + + b.Property("SenderId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("GuildId"); + + b.ToTable("GuildInvites"); + }); + + modelBuilder.Entity("Uchu.Core.InventoryItem", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("CharacterId") + .HasColumnType("INTEGER"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.Property("ExtraInfo") + .HasColumnType("TEXT"); + + b.Property("InventoryType") + .HasColumnType("INTEGER"); + + b.Property("IsBound") + .HasColumnType("INTEGER"); + + b.Property("IsEquipped") + .HasColumnType("INTEGER"); + + b.Property("Lot") + .HasColumnType("INTEGER"); + + b.Property("ParentId") + .HasColumnType("INTEGER"); + + b.Property("Slot") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.ToTable("InventoryItems"); + }); + + modelBuilder.Entity("Uchu.Core.Mission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CharacterId") + .HasColumnType("INTEGER"); + + b.Property("CompletionCount") + .HasColumnType("INTEGER"); + + b.Property("LastCompletion") + .HasColumnType("INTEGER"); + + b.Property("MissionId") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.ToTable("Missions"); + }); + + modelBuilder.Entity("Uchu.Core.MissionTask", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MissionId") + .HasColumnType("INTEGER"); + + b.Property("TaskId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MissionId"); + + b.ToTable("MissionTasks"); + }); + + modelBuilder.Entity("Uchu.Core.MissionTaskValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.Property("MissionTaskId") + .HasColumnType("INTEGER"); + + b.Property("Value") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("MissionTaskId"); + + b.ToTable("MissionTaskValue"); + }); + + modelBuilder.Entity("Uchu.Core.SessionCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CharacterId") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.Property("ZoneId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("SessionCaches"); + }); + + modelBuilder.Entity("Uchu.Core.TradeTransactionItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.Property("Party") + .HasColumnType("INTEGER"); + + b.Property("TradeId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TradeId"); + + b.ToTable("TransactionItems"); + }); + + modelBuilder.Entity("Uchu.Core.UnlockedEmote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CharacterId") + .HasColumnType("INTEGER"); + + b.Property("EmoteId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.ToTable("UnlockedEmote"); + }); + + modelBuilder.Entity("Uchu.Core.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Banned") + .HasColumnType("INTEGER"); + + b.Property("BannedReason") + .HasColumnType("TEXT"); + + b.Property("CharacterIndex") + .HasColumnType("INTEGER"); + + b.Property("CustomLockout") + .HasColumnType("TEXT"); + + b.Property("FirstTimeOnSubscription") + .HasColumnType("INTEGER"); + + b.Property("FreeToPlay") + .HasColumnType("INTEGER"); + + b.Property("GameMasterLevel") + .HasColumnType("INTEGER"); + + b.Property("Password") + .HasMaxLength(60) + .HasColumnType("TEXT"); + + b.Property("Sso") + .HasColumnType("INTEGER"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(33) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Uchu.Core.Character", b => + { + b.HasOne("Uchu.Core.User", "User") + .WithMany("Characters") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Uchu.Core.CharacterFlag", b => + { + b.HasOne("Uchu.Core.Character", "Character") + .WithMany("Flags") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("Uchu.Core.GuildInvite", b => + { + b.HasOne("Uchu.Core.Guild", "Guild") + .WithMany("Invites") + .HasForeignKey("GuildId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Guild"); + }); + + modelBuilder.Entity("Uchu.Core.InventoryItem", b => + { + b.HasOne("Uchu.Core.Character", "Character") + .WithMany("Items") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("Uchu.Core.Mission", b => + { + b.HasOne("Uchu.Core.Character", "Character") + .WithMany("Missions") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("Uchu.Core.MissionTask", b => + { + b.HasOne("Uchu.Core.Mission", "Mission") + .WithMany("Tasks") + .HasForeignKey("MissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Mission"); + }); + + modelBuilder.Entity("Uchu.Core.MissionTaskValue", b => + { + b.HasOne("Uchu.Core.MissionTask", "MissionTask") + .WithMany("Values") + .HasForeignKey("MissionTaskId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MissionTask"); + }); + + modelBuilder.Entity("Uchu.Core.TradeTransactionItem", b => + { + b.HasOne("Uchu.Core.CharacterTrade", "Trade") + .WithMany("TransactionItems") + .HasForeignKey("TradeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Trade"); + }); + + modelBuilder.Entity("Uchu.Core.UnlockedEmote", b => + { + b.HasOne("Uchu.Core.Character", "Character") + .WithMany("UnlockedEmotes") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("Uchu.Core.Character", b => + { + b.Navigation("Flags"); + + b.Navigation("Items"); + + b.Navigation("Missions"); + + b.Navigation("UnlockedEmotes"); + }); + + modelBuilder.Entity("Uchu.Core.CharacterTrade", b => + { + b.Navigation("TransactionItems"); + }); + + modelBuilder.Entity("Uchu.Core.Guild", b => + { + b.Navigation("Invites"); + }); + + modelBuilder.Entity("Uchu.Core.Mission", b => + { + b.Navigation("Tasks"); + }); + + modelBuilder.Entity("Uchu.Core.MissionTask", b => + { + b.Navigation("Values"); + }); + + modelBuilder.Entity("Uchu.Core.User", b => + { + b.Navigation("Characters"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Uchu.Core/Migrations/Sqlite/20220203130149_RacingLeaderboards.cs b/Uchu.Core/Migrations/Sqlite/20220203130149_RacingLeaderboards.cs new file mode 100644 index 00000000..46d70656 --- /dev/null +++ b/Uchu.Core/Migrations/Sqlite/20220203130149_RacingLeaderboards.cs @@ -0,0 +1,37 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Uchu.Core.Migrations.Sqlite +{ + public partial class RacingLeaderboards : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "BestLapTime", + table: "ActivityScores", + type: "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "Wins", + table: "ActivityScores", + type: "INTEGER", + nullable: false, + defaultValue: 0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "BestLapTime", + table: "ActivityScores"); + + migrationBuilder.DropColumn( + name: "Wins", + table: "ActivityScores"); + } + } +} diff --git a/Uchu.Core/Migrations/Sqlite/SqliteContextModelSnapshot.cs b/Uchu.Core/Migrations/Sqlite/SqliteContextModelSnapshot.cs index f463f670..fd52eeb1 100644 --- a/Uchu.Core/Migrations/Sqlite/SqliteContextModelSnapshot.cs +++ b/Uchu.Core/Migrations/Sqlite/SqliteContextModelSnapshot.cs @@ -5,6 +5,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Uchu.Core.Providers; +#nullable disable + namespace Uchu.Core.Migrations.Sqlite { [DbContext(typeof(SqliteContext))] @@ -13,8 +15,7 @@ partial class SqliteContextModelSnapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "5.0.5"); + modelBuilder.HasAnnotation("ProductVersion", "6.0.0"); modelBuilder.Entity("Uchu.Core.ActivityScore", b => { @@ -25,6 +26,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Activity") .HasColumnType("INTEGER"); + b.Property("BestLapTime") + .HasColumnType("INTEGER"); + b.Property("CharacterId") .HasColumnType("INTEGER"); @@ -43,6 +47,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Week") .HasColumnType("INTEGER"); + b.Property("Wins") + .HasColumnType("INTEGER"); + b.Property("Zone") .HasColumnType("INTEGER"); From 733be6f2bb446421532b686d6e22ededff2ad876 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Fri, 4 Feb 2022 22:42:18 +0100 Subject: [PATCH 49/71] Implement rewards for racing --- .../RacingControlComponent.cs | 38 +++++++++++-------- .../ScriptedActivityComponent.cs | 4 +- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 44be6021..e17eca3b 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -14,9 +14,12 @@ using System.Numerics; using System.Threading.Tasks; using InfectedRose.Luz; +using Microsoft.Scripting.Utils; using RakDotNet.IO; using Uchu.Core; +using Uchu.Core.Client; using Uchu.Physics; +using Uchu.World.Client; namespace Uchu.World { @@ -36,8 +39,6 @@ public class RacingControlComponent : ScriptedActivityComponent private MainWorldReturnData _returnData; - private int _activityId; - private uint _rankCounter = 0; public RacingControlComponent() @@ -58,7 +59,7 @@ private async Task LoadAsync() { case 1203: _deathPlaneHeight = 100; - _activityId = 42; + this.ActivityInfo = await ClientCache.FindAsync(42); _returnData = new MainWorldReturnData { ZoneId = 1200, @@ -68,7 +69,7 @@ private async Task LoadAsync() break; case 1303: _deathPlaneHeight = 0; - _activityId = 39; + this.ActivityInfo = await ClientCache.FindAsync(39); _returnData = new MainWorldReturnData { ZoneId = 1300, @@ -78,7 +79,7 @@ private async Task LoadAsync() break; case 1403: _deathPlaneHeight = 300; - _activityId = 45; + this.ActivityInfo = await ClientCache.FindAsync(54); _returnData = new MainWorldReturnData { ZoneId = 1400, @@ -97,6 +98,13 @@ private async Task LoadAsync() break; } + Rewards = ClientCache.FindAll(this.ActivityInfo.ActivityID).ToSortedList((a, b) => + { + if (a.ChallengeRating != b.ChallengeRating) + return (a.ChallengeRating ?? 1) - (b.ChallengeRating ?? 1); + return (a.ActivityRating ?? -1) - (b.ActivityRating ?? -1); + }).ToArray(); + if (_path == default) throw new Exception("Path not found"); @@ -116,7 +124,7 @@ private async Task LoadAsync() /// /// /// - private void OnMessageBoxRespond(Player player, MessageBoxRespondMessage message) + private async Task OnMessageBoxRespond(Player player, MessageBoxRespondMessage message) { Logger.Information($"Button - {message.Button} {message.Identifier} {message.UserData}"); if (message.Identifier == "ACT_RACE_EXIT_THE_RACE?" && message.Button == 1 || message.Identifier == "Exit") @@ -132,8 +140,7 @@ private void OnMessageBoxRespond(Player player, MessageBoxRespondMessage message } else if (message.Identifier == "rewardButton") { - // TODO: send rewards - relies on activityID being present in object settings - // await this.DropLootAsync(player); + await this.DropLootAsync(player, true, true); player.Message(new NotifyRacingClientMessage { @@ -520,10 +527,11 @@ private async Task PlayerReachedCheckpoint(Player player, int index) await missionInventoryComponent.RaceFinishedAsync((int)playerInfo.Finished, raceTime, (int)playerInfo.SmashedTimes); Logger.Information($"Race finished: {playerInfo.Player}, place {playerInfo.Finished}, time: {playerInfo.RaceTime.ElapsedMilliseconds}, smashed: {playerInfo.SmashedTimes}"); - - // TODO: Give rewards this.UpdateLeaderboard(playerInfo); + // Set player score for rewards + this.SetParameter(playerInfo.Player, 1, _players.Count * 10 + playerInfo.Finished); + if (_rankCounter >= _players.Count) { Logger.Information("Race ended"); @@ -549,13 +557,13 @@ private void UpdateLeaderboard(RacingPlayerInfo playerInfo) using var ctx = new UchuContext(); var existingWeekly = ctx.ActivityScores.FirstOrDefault(entry => - entry.Activity == _activityId + entry.Activity == this.ActivityInfo.ActivityID && entry.Zone == Convert.ToInt32(player.Zone.ZoneId) && entry.CharacterId == (long) player.Id && entry.Week == yearAndWeek); var existingAllTime = ctx.ActivityScores.FirstOrDefault(entry => - entry.Activity == _activityId + entry.Activity == this.ActivityInfo.ActivityID && entry.Zone == Convert.ToInt32(player.Zone.ZoneId) && entry.CharacterId == (long) player.Id && entry.Week == 0); @@ -575,7 +583,7 @@ private void UpdateLeaderboard(RacingPlayerInfo playerInfo) { ctx.ActivityScores.Add(new ActivityScore { - Activity = _activityId, + Activity = this.ActivityInfo.ActivityID ?? 0, Zone = player.Zone.ZoneId, CharacterId = player.Id, Time = raceTime, @@ -602,7 +610,7 @@ private void UpdateLeaderboard(RacingPlayerInfo playerInfo) { ctx.ActivityScores.Add(new ActivityScore { - Activity = _activityId, + Activity = this.ActivityInfo.ActivityID ?? 0, Zone = player.Zone.ZoneId, CharacterId = player.Id, Time = raceTime, @@ -678,7 +686,7 @@ private struct MainWorldReturnData { } struct RacingPlayerInfo { - public GameObject Player { get; set; } + public Player Player { get; set; } public GameObject Vehicle { get; set; } public uint PlayerIndex { get; set; } public bool PlayerLoaded { get; set; } diff --git a/Uchu.World/Objects/Components/ReplicaComponents/ScriptedActivityComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/ScriptedActivityComponent.cs index 5593bc14..5102669f 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/ScriptedActivityComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/ScriptedActivityComponent.cs @@ -20,9 +20,9 @@ public class ScriptedActivityComponent : StructReplicaComponent ComponentId.ScriptedActivityComponent; - public Activities ActivityInfo { get; private set; } + public Activities ActivityInfo { get; protected set; } - public ActivityRewards[] Rewards { get; private set; } + public ActivityRewards[] Rewards { get; protected set; } /// /// Creates the scripted activity component. From 0e61c62de25aca7551d277e943a30bafaa22f992 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Fri, 4 Feb 2022 22:44:27 +0100 Subject: [PATCH 50/71] Fix BestLapTime being 0 --- .../Components/ReplicaComponents/RacingControlComponent.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index e17eca3b..b066e2fa 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -178,7 +178,7 @@ private void OnPlayerLoad(Player player) NoSmashOnReload = true, RaceTime = new Stopwatch(), LapTime = new Stopwatch(), - BestLapTime = new TimeSpan(0), + BestLapTime = default, }); LoadPlayerCar(player); @@ -508,7 +508,7 @@ private async Task PlayerReachedCheckpoint(Player player, int index) Logger.Information($"{playerInfo.Player} now in lap {playerInfo.Lap}"); // Set new best lap if applicable - if (lapTime < playerInfo.BestLapTime.Milliseconds) + if (playerInfo.BestLapTime == default || lapTime < playerInfo.BestLapTime.Milliseconds) { playerInfo.BestLapTime = new TimeSpan(0, 0, 0, 0, lapTime); @@ -526,7 +526,7 @@ private async Task PlayerReachedCheckpoint(Player player, int index) if (playerInfo.Player.TryGetComponent(out MissionInventoryComponent missionInventoryComponent)) await missionInventoryComponent.RaceFinishedAsync((int)playerInfo.Finished, raceTime, (int)playerInfo.SmashedTimes); - Logger.Information($"Race finished: {playerInfo.Player}, place {playerInfo.Finished}, time: {playerInfo.RaceTime.ElapsedMilliseconds}, smashed: {playerInfo.SmashedTimes}"); + Logger.Information($"Race finished: {playerInfo.Player}, place {playerInfo.Finished}, time: {playerInfo.RaceTime.ElapsedMilliseconds}, smashed: {playerInfo.SmashedTimes}, best lap: {playerInfo.BestLapTime.TotalMilliseconds}"); this.UpdateLeaderboard(playerInfo); // Set player score for rewards From 62f9d67da3983d7a092f34d1c8443e7c07797a95 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Sun, 6 Feb 2022 11:36:30 +0100 Subject: [PATCH 51/71] Close race instance after all players have left --- .../RacingControlComponent.cs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index b066e2fa..d5aa30e8 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -43,7 +43,7 @@ public class RacingControlComponent : ScriptedActivityComponent public RacingControlComponent() { - _raceInfo.LapCount = 3; + _raceInfo.LapCount = 1; _raceInfo.PathName = "MainPath"; // MainPath Listen(OnStart, async () => { @@ -136,7 +136,7 @@ private async Task OnMessageBoxRespond(Player player, MessageBoxRespondMessage m SingleClient = player, }); - SendPlayerToMainWorld(player); + await SendPlayerToMainWorldAsync(player); } else if (message.Identifier == "rewardButton") { @@ -155,19 +155,19 @@ private async Task OnMessageBoxRespond(Player player, MessageBoxRespondMessage m /// This runs when the player loads into the world, it registers the player /// /// - private void OnPlayerLoad(Player player) + private async Task OnPlayerLoad(Player player) { Logger.Information("Player loaded into racing"); // If race has already started return player to main world if (_racingStatus != RacingStatus.None) { - SendPlayerToMainWorld(player); + await SendPlayerToMainWorldAsync(player); return; } if (player.TryGetComponent(out MissionInventoryComponent missionInventoryComponent)) - missionInventoryComponent.RacingEnterWorld(this.GameObject.Zone.ZoneId); + await missionInventoryComponent.RacingEnterWorld(this.GameObject.Zone.ZoneId); // Register player this._players.Add(new RacingPlayerInfo @@ -198,10 +198,16 @@ private void OnPlayerLoad(Player player) /// Send the player back to the world he came from /// /// - private void SendPlayerToMainWorld(Player player) + private async Task SendPlayerToMainWorldAsync(Player player) { _players.RemoveAll(info => info.Player == player); - player.SendToWorldAsync(_returnData.ZoneId, _returnData.Position, _returnData.Rotation); + await player.SendToWorldAsync(_returnData.ZoneId, _returnData.Position, _returnData.Rotation); + + if (_players.Count == 0) { + await Task.Delay(10000); + await this.GameObject.UchuServer.StopAsync(); + Logger.Debug("Closed Server"); + } } /// @@ -535,7 +541,6 @@ private async Task PlayerReachedCheckpoint(Player player, int index) if (_rankCounter >= _players.Count) { Logger.Information("Race ended"); - // TODO: close instance } } } From 98aef450d3a1d7c39023436b1b9a0b989fee6127 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Thu, 10 Feb 2022 14:48:59 +0100 Subject: [PATCH 52/71] Add description to _arrayNoLenght attribute --- Uchu.Core/Serializable/Struct/Property/PacketProperty.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Uchu.Core/Serializable/Struct/Property/PacketProperty.cs b/Uchu.Core/Serializable/Struct/Property/PacketProperty.cs index af362876..bc621f50 100644 --- a/Uchu.Core/Serializable/Struct/Property/PacketProperty.cs +++ b/Uchu.Core/Serializable/Struct/Property/PacketProperty.cs @@ -92,6 +92,10 @@ public class PacketProperty : IPacketProperty /// private Type _arrayLengthPropertyType = typeof(uint); + /// + /// A special way of sending arrays without a lenght attribute by + /// having a bit after each item that indicates if the array ends + /// private bool _arrayNoLenght = false; /// From 6987ea94f14600bac4472fd795ecb6b676680551 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Thu, 10 Feb 2022 15:15:09 +0100 Subject: [PATCH 53/71] Implement PostRacePlayerInfos --- .../Components/ReplicaComponents/RacingControlComponent.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index d5aa30e8..f5de80a7 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -546,6 +546,7 @@ private async Task PlayerReachedCheckpoint(Player player, int index) } _players[playerInfoIndex] = playerInfo; + GameObject.Serialize(this.GameObject); Logger.Information($"Player reached checkpoint: {index}"); } @@ -673,7 +674,11 @@ public override void Serialize(BitWriter writer) RaceTime = (float) info.RaceTime.Elapsed.TotalSeconds, }).ToArray(); - // TODO + packet.PostRacePlayerInfos = this._players.Select(info => new PostRacePlayerInfo + { + Player = info.Player, + CurrentRank = info.Finished + }).ToArray(); return packet; } From 29d3597664a013d4979109bd85e491b8c3f17f3f Mon Sep 17 00:00:00 2001 From: TecCheck Date: Thu, 10 Feb 2022 15:15:41 +0100 Subject: [PATCH 54/71] Format RacingControlComponent --- .../RacingControlComponent.cs | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index f5de80a7..da693d1c 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -174,7 +174,7 @@ private async Task OnPlayerLoad(Player player) { Player = player, PlayerLoaded = true, - PlayerIndex = (uint) _players.Count + 1, + PlayerIndex = (uint)_players.Count + 1, NoSmashOnReload = true, RaceTime = new Stopwatch(), LapTime = new Stopwatch(), @@ -203,7 +203,8 @@ private async Task SendPlayerToMainWorldAsync(Player player) _players.RemoveAll(info => info.Player == player); await player.SendToWorldAsync(_returnData.ZoneId, _returnData.Position, _returnData.Rotation); - if (_players.Count == 0) { + if (_players.Count == 0) + { await Task.Delay(10000); await this.GameObject.UchuServer.StopAsync(); Logger.Debug("Closed Server"); @@ -221,7 +222,7 @@ private void LoadPlayerCar(Player player) var waypoint = (LuzRaceWaypoint)_path.Waypoints.First(); var startPosition = waypoint.Position; var startRotation = waypoint.Rotation; - + var spacing = 15; var positionOffset = startRotation.VectorMultiply(Vector3.UnitX) * _players.Count * spacing; startPosition += positionOffset + Vector3.UnitY * 3; @@ -470,11 +471,12 @@ public void OnPlayerRequestDie(Player player) Associate = racingPlayer.Vehicle, }, player); - Zone.Schedule(() => { + Zone.Schedule(() => + { Zone.BroadcastMessage(new RacingSetPlayerResetInfoMessage { Associate = GameObject, - CurrentLap = (int) racingPlayer.Lap, + CurrentLap = (int)racingPlayer.Lap, FurthestResetPlane = racingPlayer.RespawnIndex, PlayerId = player, RespawnPos = racingPlayer.RespawnPosition + Vector3.UnitY * 5, @@ -565,13 +567,13 @@ private void UpdateLeaderboard(RacingPlayerInfo playerInfo) var existingWeekly = ctx.ActivityScores.FirstOrDefault(entry => entry.Activity == this.ActivityInfo.ActivityID && entry.Zone == Convert.ToInt32(player.Zone.ZoneId) - && entry.CharacterId == (long) player.Id + && entry.CharacterId == (long)player.Id && entry.Week == yearAndWeek); var existingAllTime = ctx.ActivityScores.FirstOrDefault(entry => entry.Activity == this.ActivityInfo.ActivityID && entry.Zone == Convert.ToInt32(player.Zone.ZoneId) - && entry.CharacterId == (long) player.Id + && entry.CharacterId == (long)player.Id && entry.Week == 0); // Update existing weekly leaderboard entry @@ -670,8 +672,8 @@ public override void Serialize(BitWriter writer) packet.DuringRacePlayerInfos = this._players.Select(info => new DuringRacePlayerInfo { Player = info.Player, - BestLapTime = (float) info.BestLapTime.TotalSeconds, - RaceTime = (float) info.RaceTime.Elapsed.TotalSeconds, + BestLapTime = (float)info.BestLapTime.TotalSeconds, + RaceTime = (float)info.RaceTime.Elapsed.TotalSeconds, }).ToArray(); packet.PostRacePlayerInfos = this._players.Select(info => new PostRacePlayerInfo @@ -683,19 +685,22 @@ public override void Serialize(BitWriter writer) return packet; } - private enum RacingStatus { + private enum RacingStatus + { None, Loaded, Started, } - private struct MainWorldReturnData { + private struct MainWorldReturnData + { public ZoneId ZoneId { get; set; } public Vector3 Position { get; set; } public Quaternion Rotation { get; set; } } - struct RacingPlayerInfo { + struct RacingPlayerInfo + { public Player Player { get; set; } public GameObject Vehicle { get; set; } public uint PlayerIndex { get; set; } From 709152bfa6af5421c3592b2e76dd299629c30fb6 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Sat, 19 Feb 2022 16:42:04 +0100 Subject: [PATCH 55/71] Refactor RacingTask --- Uchu.World/Enums/RacingTaskType.cs | 20 +++ .../Handlers/GameMessages/RacingHandler.cs | 2 +- .../Player/MissionInventoryComponent.cs | 45 +++--- .../RacingControlComponent.cs | 7 +- Uchu.World/Systems/Missions/RacingTask.cs | 145 +++++++----------- 5 files changed, 105 insertions(+), 114 deletions(-) create mode 100644 Uchu.World/Enums/RacingTaskType.cs diff --git a/Uchu.World/Enums/RacingTaskType.cs b/Uchu.World/Enums/RacingTaskType.cs new file mode 100644 index 00000000..76e91811 --- /dev/null +++ b/Uchu.World/Enums/RacingTaskType.cs @@ -0,0 +1,20 @@ +namespace Uchu.World +{ + public enum RacingTaskType : int + { + Rank = 1, + Laptime = 2, + Racetime = 3, + Missions = 4, + ZoneMissions = 5, + SwapParts = 6, // Unused? + Wrecks = 10, + SmashAny = 11, + Imagination = 12, + EnterWorld = 13, + WinInWorld = 14, + WinInWorlds = 15, + FinishLast = 16, + Smash = 17 + } +} \ No newline at end of file diff --git a/Uchu.World/Handlers/GameMessages/RacingHandler.cs b/Uchu.World/Handlers/GameMessages/RacingHandler.cs index 8c3518cd..30964731 100644 --- a/Uchu.World/Handlers/GameMessages/RacingHandler.cs +++ b/Uchu.World/Handlers/GameMessages/RacingHandler.cs @@ -30,7 +30,7 @@ public async void ImaginationHandler(VehicleNotifyHitImaginationServerMessage me // Progress imagination collect task if (message.Associate.TryGetComponent(out PossessableComponent possessableComponent)) if (possessableComponent.Driver.TryGetComponent(out MissionInventoryComponent missionInventoryComponent)) - await missionInventoryComponent.RacingCollectImaginationAsync(); + await missionInventoryComponent.RacingCollectImaginationAsync(player.Zone.ZoneId); } [PacketHandler] diff --git a/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs b/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs index a9b414ce..738b90e1 100644 --- a/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs +++ b/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs @@ -706,35 +706,29 @@ await StartUnlockableAchievementsAsync(MissionTaskType.TamePet, Pet }); } - public async Task RaceFinishedAsync(int rank, int racetime, int wrecks) + public async Task RaceFinishedAsync(ZoneId zoneId, uint rank, long racetime, uint wrecks, int playerCount) { foreach (var task in FindActiveTasksAsync()) { - await task.ReportRank(rank, this.GameObject.Zone.ZoneId); - await task.ReportRacetime(racetime, this.GameObject.Zone.ZoneId); - await task.ReportWreck(wrecks, this.GameObject.Zone.ZoneId); - if (rank == 1) await task.ReportWin(this.GameObject.Zone.ZoneId); + await task.ReportRaceDone(zoneId, rank, racetime, wrecks, playerCount); } - await StartUnlockableAchievementsAsync(MissionTaskType.Racing, 0, async task => + await StartUnlockableAchievementsAsync(MissionTaskType.Racing, (int)zoneId, async task => { - await task.ReportRank(rank, this.GameObject.Zone.ZoneId); - await task.ReportRacetime(racetime, this.GameObject.Zone.ZoneId); - await task.ReportWreck(wrecks, this.GameObject.Zone.ZoneId); - if (rank == 1) await task.ReportWin(this.GameObject.Zone.ZoneId); + await task.ReportRaceDone(zoneId, rank, racetime, wrecks, playerCount); }); } - public async Task RacingLaptimeAsync(int laptime) + public async Task RacingLaptimeAsync(ZoneId zoneId, int laptime) { foreach (var task in FindActiveTasksAsync()) { - await task.ReportLaptime(laptime, this.GameObject.Zone.ZoneId); + await task.ReportLaptime(laptime, zoneId); } - await StartUnlockableAchievementsAsync(MissionTaskType.Racing, laptime, async task => + await StartUnlockableAchievementsAsync(MissionTaskType.Racing, (int)zoneId, async task => { - await task.ReportLaptime(laptime, this.GameObject.Zone.ZoneId); + await task.ReportLaptime(laptime, zoneId); }); } @@ -751,16 +745,16 @@ await StartUnlockableAchievementsAsync(MissionTaskType.Racing, missi }); } - public async Task RacingCollectImaginationAsync() + public async Task RacingCollectImaginationAsync(ZoneId zoneId) { foreach (var task in FindActiveTasksAsync()) { - await task.ReportImagination(this.GameObject.Zone.ZoneId); + await task.ReportImagination(zoneId); } - await StartUnlockableAchievementsAsync(MissionTaskType.Racing, Lot.Imagination, async task => + await StartUnlockableAchievementsAsync(MissionTaskType.Racing, (int)zoneId, async task => { - await task.ReportImagination(this.GameObject.Zone.ZoneId); + await task.ReportImagination(zoneId); }); } @@ -773,20 +767,25 @@ public async Task RacingEnterWorld(ZoneId zoneId) await StartUnlockableAchievementsAsync(MissionTaskType.Racing, (int)zoneId, async task => { - await task.ReportImagination(zoneId); + await task.ReportWorldEnter(zoneId); }); } - public async Task RacingSmashAsync(Lot lot) + public async Task RacingSmashAsync(Lot lot, ZoneId zoneId) { foreach (var task in FindActiveTasksAsync()) { - await task.ReportSmash(lot); + await task.ReportSmash(lot, zoneId); } await StartUnlockableAchievementsAsync(MissionTaskType.Racing, lot, async task => { - await task.ReportSmash(lot);; + await task.ReportSmash(lot, zoneId); + }); + + await StartUnlockableAchievementsAsync(MissionTaskType.Racing, (int)zoneId, async task => + { + await task.ReportSmash(lot, zoneId); }); } @@ -803,7 +802,7 @@ await StartUnlockableAchievementsAsync(MissionTaskType.Racing, lot, /// A list of all the achievements a player can unlock, given the task type and the lot private MissionInstance[] UnlockableAchievements(MissionTaskType type, Lot lot) where T : MissionTaskInstance => ClientCache.Achievements.Where(m => - m.Tasks.OfType().Any(t => t.Type == type && (t.Targets.Contains((int) lot) || t.Parameters.Contains((int) lot))) + m.Tasks.OfType().Any(t => t.Type == type && (t.Targets.Contains((int)lot) || t.Parameters.Contains((int)lot))) && CanAccept(m)).ToArray(); /// diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index da693d1c..8e76766b 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -521,7 +521,7 @@ private async Task PlayerReachedCheckpoint(Player player, int index) playerInfo.BestLapTime = new TimeSpan(0, 0, 0, 0, lapTime); if (playerInfo.Player.TryGetComponent(out MissionInventoryComponent missionInventoryComponent)) - await missionInventoryComponent.RacingLaptimeAsync(lapTime); + await missionInventoryComponent.RacingLaptimeAsync(Zone.ZoneId, lapTime); } // If player finished race @@ -532,7 +532,10 @@ private async Task PlayerReachedCheckpoint(Player player, int index) playerInfo.Finished = ++_rankCounter; if (playerInfo.Player.TryGetComponent(out MissionInventoryComponent missionInventoryComponent)) - await missionInventoryComponent.RaceFinishedAsync((int)playerInfo.Finished, raceTime, (int)playerInfo.SmashedTimes); + { + await missionInventoryComponent.RaceFinishedAsync(Zone.ZoneId, playerInfo.Finished, + playerInfo.RaceTime.ElapsedMilliseconds, playerInfo.SmashedTimes, _players.Count); + } Logger.Information($"Race finished: {playerInfo.Player}, place {playerInfo.Finished}, time: {playerInfo.RaceTime.ElapsedMilliseconds}, smashed: {playerInfo.SmashedTimes}, best lap: {playerInfo.BestLapTime.TotalMilliseconds}"); this.UpdateLeaderboard(playerInfo); diff --git a/Uchu.World/Systems/Missions/RacingTask.cs b/Uchu.World/Systems/Missions/RacingTask.cs index 51879d34..e36eaa3c 100644 --- a/Uchu.World/Systems/Missions/RacingTask.cs +++ b/Uchu.World/Systems/Missions/RacingTask.cs @@ -15,140 +15,109 @@ public RacingTask(MissionInstance mission, int taskId, int missionTaskIndex) public override bool Completed => IsCompleted(); - public async Task ReportRank(int place, ZoneId zoneId) + public async Task ReportRaceDone(ZoneId zoneId, uint place, long racetime, uint wrecks, int playerCount) { - if (!IsValid(TaskType.Rank, zoneId)) + if (!Targets.Contains(zoneId)) return; - await ReportProgressValue(place, zoneId); + int minPlayerCount = 0; // TODO: 3; + switch ((RacingTaskType)Parameters[0]) + { + case RacingTaskType.Rank: + if (place <= RequiredProgress && playerCount >= minPlayerCount) + await AddProgressAndCheck(zoneId); + break; + + case RacingTaskType.Racetime: + if (racetime <= RequiredProgress) + await AddProgressAndCheck(zoneId); + break; + + case RacingTaskType.Wrecks: + if (wrecks <= 0) + await AddProgressAndCheck(zoneId); + break; + + case RacingTaskType.WinInWorld: + case RacingTaskType.WinInWorlds: + if (place <= 1 && playerCount >= minPlayerCount) + await AddProgressAndCheck(zoneId); + break; + + case RacingTaskType.FinishLast: + if (place >= playerCount) + await AddProgressAndCheck(zoneId); + break; + } } public async Task ReportLaptime(int laptime, ZoneId zoneId) { - if (!IsValid(TaskType.Laptime, zoneId)) - return; - - await ReportProgressValue(laptime, zoneId); - } - - public async Task ReportRacetime(int racetime, ZoneId zoneId) - { - if (!IsValid(TaskType.Racetime, zoneId)) + if (!IsValid(RacingTaskType.Laptime, zoneId)) return; - await ReportProgressValue(racetime, zoneId); - } - - private async Task ReportProgressValue(int value, ZoneId zoneId) - { - if (value <= RequiredProgress) - AddProgress(zoneId); - - if (Completed) - await CheckMissionCompletedAsync(); + if (laptime <= RequiredProgress) + await AddProgressAndCheck(zoneId); } public async Task ReportMissionComplete(int missionId) { - if (!IsValid(TaskType.Achievements, missionId)) + var taskType = Parameters[0]; + if (taskType != (int)RacingTaskType.Missions && taskType != (int)RacingTaskType.ZoneMissions) return; - AddProgress(missionId); + if (!Targets.Contains(missionId) || Progress.Contains(missionId)) + return; - if (Completed) - await CheckMissionCompletedAsync(); + await AddProgressAndCheck(missionId); } - public async Task ReportWreck(int wrecks, ZoneId zoneId) + public async Task ReportSmash(Lot lot, ZoneId zoneId) { - if (!IsValid(TaskType.Wrecks, zoneId)) - return; - - if (wrecks >= RequiredProgress) - return; - - AddProgress(zoneId); - - if (Completed) - await CheckMissionCompletedAsync(); + if (IsValid(RacingTaskType.SmashAny, zoneId)) + await AddProgressAndCheck(zoneId); + else if (IsValid(RacingTaskType.Smash, lot)) + await AddProgressAndCheck(lot); } public async Task ReportImagination(ZoneId zoneId) { - if (!IsValid(TaskType.Imagination, zoneId)) + if (!IsValid(RacingTaskType.Imagination, zoneId)) return; - AddProgress(zoneId); - - if (Completed) - await CheckMissionCompletedAsync(); + await AddProgressAndCheck(zoneId); } public async Task ReportWorldEnter(ZoneId zoneId) { - if (!IsValid(TaskType.EnterWorld, zoneId)) - return; - - AddProgress(zoneId); - - if (Completed) - await CheckMissionCompletedAsync(); - } - - public async Task ReportWin(ZoneId zoneId) - { - if (Parameters[0] != (int)TaskType.WinInWorld && Parameters[0] != (int)TaskType.WinInWorlds) - return; - - if (!Targets.Contains(zoneId)) + if (!IsValid(RacingTaskType.EnterWorld, zoneId)) return; - AddProgress(zoneId); - - if (Completed) - await CheckMissionCompletedAsync(); + await AddProgressAndCheck(zoneId); } - public async Task ReportSmash(Lot lot) + private async Task AddProgressAndCheck(float value) { - if (!IsValid(TaskType.Smash, lot)) - return; - - AddProgress(lot); - + AddProgress(value); if (Completed) await CheckMissionCompletedAsync(); } private bool IsCompleted() { - switch ((TaskType)Parameters[0]) + switch ((RacingTaskType)Parameters[0]) { - case TaskType.Rank: - case TaskType.Laptime: - case TaskType.Racetime: - return Progress.Contains(Target); + case RacingTaskType.Rank: + case RacingTaskType.Laptime: + case RacingTaskType.Racetime: + return CurrentProgress > 0; default: return base.Completed; } } - - private bool IsValid(TaskType taskType, int target) => + + private bool IsValid(RacingTaskType taskType, int target) => Parameters[0] == (int)taskType && Targets.Contains(target); - - private enum TaskType : int - { - Rank = 1, - Laptime = 2, - Racetime = 3, - Achievements = 5, - Wrecks = 10, - Imagination = 12, - EnterWorld = 13, - WinInWorld = 14, - WinInWorlds = 15, - Smash = 17 - } } } From 3f762341fef1128b766b31dba1629ae1599d7265 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Sun, 20 Feb 2022 14:09:29 +0100 Subject: [PATCH 56/71] Implement last racing task types --- .../Player/MissionInventoryComponent.cs | 22 ++++++++++++++++++- .../DestructibleComponent.cs | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs b/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs index e21ec46e..aaae64e4 100644 --- a/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs +++ b/Uchu.World/Objects/Components/Player/MissionInventoryComponent.cs @@ -429,7 +429,7 @@ private IEnumerable FindActiveTasksAsync() where T : MissionTaskInstance = /// Progresses all the smash tasks using the provided lot /// /// The lot to progress the smash tasks with - public async Task SmashAsync(Lot lot) + public async Task SmashAsync(Lot lot, ZoneId zoneId) { foreach (var task in FindActiveTasksAsync()) { @@ -440,6 +440,16 @@ await StartUnlockableAchievementsAsync(MissionTaskType.Smash, lot, as { await task.ReportProgress(lot); }); + + foreach (var task in FindActiveTasksAsync()) + { + await task.ReportSmash(lot, zoneId); + } + + await StartUnlockableAchievementsAsync(MissionTaskType.Racing, (int)zoneId, async task => + { + await task.ReportSmash(lot, zoneId); + }); } /// @@ -669,6 +679,16 @@ await StartUnlockableAchievementsAsync(MissionTaskType.Miss { await task.ReportProgress(id); }); + + foreach (var task in FindActiveTasksAsync()) + { + await task.ReportMissionComplete(id); + } + + await StartUnlockableAchievementsAsync(MissionTaskType.Racing, id, async task => + { + await task.ReportMissionComplete(id); + }); } /// diff --git a/Uchu.World/Objects/Components/ReplicaComponents/DestructibleComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/DestructibleComponent.cs index 67cf9d17..089a7293 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/DestructibleComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/DestructibleComponent.cs @@ -120,7 +120,7 @@ public async Task SmashAsync(GameObject smasher, Player owner = default, string // Check achievements and missions if (owner != null && owner.TryGetComponent(out var missionInventory)) { - await missionInventory.SmashAsync(GameObject.Lot); + await missionInventory.SmashAsync(GameObject.Lot, Zone.ZoneId); owner.OnSmashObject.Invoke(GameObject); } From 66a8cc54664a01d6d3690bccaa65be42884ee34b Mon Sep 17 00:00:00 2001 From: frozenreflex Date: Sun, 20 Feb 2022 14:51:37 -0600 Subject: [PATCH 57/71] Script changes --- .../General/DeathPlane/DeathPlane.cs | 2 +- .../General/DeathPlane/PlayerFallDeath.cs | 2 +- .../General/DeathPlane/PropertyDeathPlane.cs | 2 +- .../General/DeathPlane/SharkDeathPlane.cs | 2 +- .../General/DeathPlane/WorldDeathPlane.cs | 18 +++++++----------- .../Racing/ForbiddenValleyEggs.cs | 7 +++---- .../Racing/ImaginationCrate.cs | 7 +++---- 7 files changed, 17 insertions(+), 23 deletions(-) diff --git a/Uchu.StandardScripts/General/DeathPlane/DeathPlane.cs b/Uchu.StandardScripts/General/DeathPlane/DeathPlane.cs index 7e6e3be0..2770e996 100644 --- a/Uchu.StandardScripts/General/DeathPlane/DeathPlane.cs +++ b/Uchu.StandardScripts/General/DeathPlane/DeathPlane.cs @@ -20,7 +20,7 @@ public DeathPlane(GameObject gameObject) : base(gameObject) if (physics == default) return; Listen(physics.OnEnter, other => { - if (!(other.GameObject is Player player)) return; + if (other.GameObject is not Player player) return; Task.Run(async () => { await player.GetComponent().SmashAsync(gameObject); diff --git a/Uchu.StandardScripts/General/DeathPlane/PlayerFallDeath.cs b/Uchu.StandardScripts/General/DeathPlane/PlayerFallDeath.cs index 6b720afe..b276f1b6 100644 --- a/Uchu.StandardScripts/General/DeathPlane/PlayerFallDeath.cs +++ b/Uchu.StandardScripts/General/DeathPlane/PlayerFallDeath.cs @@ -20,7 +20,7 @@ public PlayerFallDeath(GameObject gameObject) : base(gameObject) if (physics == default) return; Listen(physics.OnEnter, other => { - if (!(other.GameObject is Player player)) return; + if (other.GameObject is not Player player) return; Task.Run(async () => { await Task.Delay(2000); diff --git a/Uchu.StandardScripts/General/DeathPlane/PropertyDeathPlane.cs b/Uchu.StandardScripts/General/DeathPlane/PropertyDeathPlane.cs index 9fee430f..2f69dc69 100644 --- a/Uchu.StandardScripts/General/DeathPlane/PropertyDeathPlane.cs +++ b/Uchu.StandardScripts/General/DeathPlane/PropertyDeathPlane.cs @@ -19,7 +19,7 @@ public PropertyDeathPlane(GameObject gameObject) : base(gameObject) if (physics == default) return; Listen(physics.OnEnter, other => { - if (!(other.GameObject is Player player)) return; + if (other.GameObject is not Player player) return; var teleportObject = this.GetGroup("Teleport")[0]; player.Teleport(teleportObject.Transform.Position, ignore: false); }); diff --git a/Uchu.StandardScripts/General/DeathPlane/SharkDeathPlane.cs b/Uchu.StandardScripts/General/DeathPlane/SharkDeathPlane.cs index b9badff9..adff9a8d 100644 --- a/Uchu.StandardScripts/General/DeathPlane/SharkDeathPlane.cs +++ b/Uchu.StandardScripts/General/DeathPlane/SharkDeathPlane.cs @@ -22,7 +22,7 @@ public SharkDeathPlane(GameObject gameObject) : base(gameObject) if (physics == default) return; Listen(physics.OnEnter, other => { - if (!(other.GameObject is Player player)) return; + if (other.GameObject is not Player player) return; var character = player.GetComponent(); var missionInventoryComponent = player.GetComponent(); Task.Run(async () => diff --git a/Uchu.StandardScripts/General/DeathPlane/WorldDeathPlane.cs b/Uchu.StandardScripts/General/DeathPlane/WorldDeathPlane.cs index 6d0c1337..74e0a40f 100644 --- a/Uchu.StandardScripts/General/DeathPlane/WorldDeathPlane.cs +++ b/Uchu.StandardScripts/General/DeathPlane/WorldDeathPlane.cs @@ -9,23 +9,19 @@ namespace Uchu.StandardScripts.General.DeathPlane { public class WorldDeathPlane: NativeScript { + private static readonly Dictionary Heights = new(){ + {1300, 80}, + {1400, -200}, + {1800, -100}, + }; public override Task LoadAsync() { List dead = new List(); - Dictionary heights = new Dictionary(){ - {1300, 80}, - {1400, -200}, - {1800, -100}, - //if we decide that -100 is fine for every world, switch this to a - //list containing just the zoneids, replace the if check below with - //heights.contains, and replace height in the listen statements - //with -100 - }; - if (!heights.ContainsKey(Zone.ZoneId)) + if (!Heights.ContainsKey(Zone.ZoneId)) { return Task.CompletedTask; } - var height = heights[Zone.ZoneId]; + var height = Heights[Zone.ZoneId]; Listen(Zone.OnPlayerLoad, player => { var destructibleComponent = player.GetComponent(); diff --git a/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs b/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs index 53bdd773..92fd93a9 100644 --- a/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs +++ b/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs @@ -16,10 +16,9 @@ public ForbiddenValleyEggs(GameObject gameObject) : base(gameObject) var car = player.GetComponent().VehicleObject; if (car is null) return; - - // it's meant to be skill 586... - var destroyableComponent = car.GetComponent(); - destroyableComponent.Imagination += 10; + + var skillComponent = car.GetComponent(); + skillComponent.CalculateSkillAsync(586, car); }); } } diff --git a/Uchu.StandardScripts/Racing/ImaginationCrate.cs b/Uchu.StandardScripts/Racing/ImaginationCrate.cs index dfc9ccaa..ee82f4b8 100644 --- a/Uchu.StandardScripts/Racing/ImaginationCrate.cs +++ b/Uchu.StandardScripts/Racing/ImaginationCrate.cs @@ -16,10 +16,9 @@ public ImaginationCrate(GameObject gameObject) : base(gameObject) var car = player.GetComponent().VehicleObject; if (car is null) return; - - // it's meant to be skill 585 but i could not get that to work - var destroyableComponent = car.GetComponent(); - destroyableComponent.Imagination += 100; + + var skillComponent = car.GetComponent(); + skillComponent.CalculateSkillAsync(585, car); }); } } From a9daed9008f91c0677e82bb151dfe1d7f053685e Mon Sep 17 00:00:00 2001 From: frozenreflex Date: Sun, 20 Feb 2022 15:07:24 -0600 Subject: [PATCH 58/71] Add progression for "Dragonscourge" and "Terror of Dragonmaw Chasm" --- Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs | 4 +++- Uchu.StandardScripts/Racing/ImaginationCrate.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs b/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs index 92fd93a9..85e156df 100644 --- a/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs +++ b/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs @@ -8,7 +8,7 @@ public class ForbiddenValleyEggs : ObjectScript { public ForbiddenValleyEggs(GameObject gameObject) : base(gameObject) { - this.Listen(gameObject.GetComponent().OnSmashed, (killer, lootOwner) => + Listen(gameObject.GetComponent().OnSmashed, (killer, lootOwner) => { if (killer is not Player player) return; @@ -19,6 +19,8 @@ public ForbiddenValleyEggs(GameObject gameObject) : base(gameObject) var skillComponent = car.GetComponent(); skillComponent.CalculateSkillAsync(586, car); + if (player.TryGetComponent(out var missionInventoryComponent)) + missionInventoryComponent.RacingSmashAsync(gameObject.Lot, 1404); }); } } diff --git a/Uchu.StandardScripts/Racing/ImaginationCrate.cs b/Uchu.StandardScripts/Racing/ImaginationCrate.cs index ee82f4b8..4fccee2c 100644 --- a/Uchu.StandardScripts/Racing/ImaginationCrate.cs +++ b/Uchu.StandardScripts/Racing/ImaginationCrate.cs @@ -8,7 +8,7 @@ public class ImaginationCrate : ObjectScript { public ImaginationCrate(GameObject gameObject) : base(gameObject) { - this.Listen(gameObject.GetComponent().OnSmashed, (killer, lootOwner) => + Listen(gameObject.GetComponent().OnSmashed, (killer, lootOwner) => { if (killer is not Player player) return; From 1dc606d72d21ffd2213c3d8473827f62afff290f Mon Sep 17 00:00:00 2001 From: frozenreflex Date: Sun, 20 Feb 2022 15:31:01 -0600 Subject: [PATCH 59/71] Add blue mark and add race_imagine_powerup to script blacklist remove the scripted smash task because I realized this should be done already --- .../Racing/ForbiddenValleyEggs.cs | 2 -- .../Racing/MinigameBlueMark.cs | 19 +++++++++++++++++++ Uchu.World/Objects/Zone.cs | 3 +++ 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 Uchu.StandardScripts/Racing/MinigameBlueMark.cs diff --git a/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs b/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs index 85e156df..cc5dd462 100644 --- a/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs +++ b/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs @@ -19,8 +19,6 @@ public ForbiddenValleyEggs(GameObject gameObject) : base(gameObject) var skillComponent = car.GetComponent(); skillComponent.CalculateSkillAsync(586, car); - if (player.TryGetComponent(out var missionInventoryComponent)) - missionInventoryComponent.RacingSmashAsync(gameObject.Lot, 1404); }); } } diff --git a/Uchu.StandardScripts/Racing/MinigameBlueMark.cs b/Uchu.StandardScripts/Racing/MinigameBlueMark.cs new file mode 100644 index 00000000..c63073d9 --- /dev/null +++ b/Uchu.StandardScripts/Racing/MinigameBlueMark.cs @@ -0,0 +1,19 @@ +using Uchu.World; +using Uchu.World.Scripting.Native; + +namespace Uchu.StandardScripts.Racing; + +[ScriptName("minigame_blue_mark.lua")] +public class MinigameBlueMark : ObjectScript +{ + public MinigameBlueMark(GameObject gameObject) : base(gameObject) + { + Listen(gameObject.OnStart, () => + { + Zone.BroadcastMessage(new NotifyClientObjectMessage + { + Name = "Blue_Mark" + }); + }); + } +} \ No newline at end of file diff --git a/Uchu.World/Objects/Zone.cs b/Uchu.World/Objects/Zone.cs index c945709f..f1a072f4 100644 --- a/Uchu.World/Objects/Zone.cs +++ b/Uchu.World/Objects/Zone.cs @@ -104,6 +104,9 @@ public class Zone : Object //function done by LaunchpadEvent.cs "l_ag_zone_player.lua", + + //appears to actually do nothing? + "race_imagine_powerup.lua", }; public Zone(ZoneInfo zoneInfo, WorldUchuServer server, ushort instanceId = default, uint cloneId = default) From 84ca188642ed7d7ec33878d851c30577596cd4c0 Mon Sep 17 00:00:00 2001 From: frozenreflex Date: Sun, 20 Feb 2022 16:46:12 -0600 Subject: [PATCH 60/71] Add maelstrom geiser script --- .../Racing/MaelstromGeiser.cs | 52 +++++++++++++++++++ .../RacingControlComponent.cs | 11 ++-- Uchu.World/Objects/Zone.cs | 3 ++ 3 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 Uchu.StandardScripts/Racing/MaelstromGeiser.cs diff --git a/Uchu.StandardScripts/Racing/MaelstromGeiser.cs b/Uchu.StandardScripts/Racing/MaelstromGeiser.cs new file mode 100644 index 00000000..9b3eb663 --- /dev/null +++ b/Uchu.StandardScripts/Racing/MaelstromGeiser.cs @@ -0,0 +1,52 @@ +using Uchu.Core; +using Uchu.World; +using Uchu.World.Scripting.Native; + +namespace Uchu.StandardScripts.Racing; + +//reimplemented from DLU +[ScriptName("race_maelstrom_geiser.lua")] +public class MaelstromGeiser : ObjectScript +{ + private bool _firing; + public MaelstromGeiser(GameObject gameObject) : base(gameObject) + { + SetProximityRadius(15, "deathZone"); + if (!gameObject.Settings.TryGetValue("startTime", out var time)) return; + AddTimerWithCancel((int)time, "downTime"); + } + public override void OnProximityUpdate(string name, PhysicsCollisionStatus status, Player player) + { + if (player == default || name != "deathZone" || status != PhysicsCollisionStatus.Enter || !_firing) return; + var car = player.GetComponent().VehicleObject; + if (!Zone.ZoneControlObject.TryGetComponent(out var racingControlComponent)) return; + Zone.BroadcastMessage(new DieMessage + { + Associate = car, + Killer = GameObject, + KillType = KillType.Violent, + ClientDeath = true, + SpawnLoot = false, + }); + racingControlComponent.OnPlayerRequestDie(player); + } + public override void OnTimerDone(string timerName) + { + switch (timerName) + { + case "downTime": + PlayFXEffect("geiser", "rebuild_medium", 4048); + AddTimerWithCancel(1, "buildUpTime"); + break; + case "buildUpTime": + _firing = true; + AddTimerWithCancel(1.5f, "killTime"); + break; + case "killTime": + StopFXEffect("geiser"); + _firing = false; + AddTimerWithCancel(3, "downTime"); + break; + } + } +} \ No newline at end of file diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 8e76766b..9d44678f 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -166,7 +166,7 @@ private async Task OnPlayerLoad(Player player) return; } - if (player.TryGetComponent(out MissionInventoryComponent missionInventoryComponent)) + if (player.TryGetComponent(out var missionInventoryComponent)) await missionInventoryComponent.RacingEnterWorld(this.GameObject.Zone.ZoneId); // Register player @@ -247,10 +247,9 @@ private void LoadPlayerCar(Player player) var inventory = player.GetComponent(); var carItem = inventory.FindItem(Lot.ModularCar); var moduleAssemblyComponent = car.GetComponent(); - if (carItem == null) - moduleAssemblyComponent.SetAssembly("1:8129;1:8130;1:13513;1:13512;1:13515;1:13516;1:13514;"); // Fallback - else - moduleAssemblyComponent.SetAssembly(carItem.Settings["assemblyPartLOTs"].ToString()); + moduleAssemblyComponent.SetAssembly(carItem == null + ? "1:8129;1:8130;1:13513;1:13512;1:13515;1:13516;1:13514;" + : carItem.Settings["assemblyPartLOTs"].ToString()); Start(car); GameObject.Construct(car); @@ -357,7 +356,7 @@ private void InitRace() }); this.Listen(physics.OnLeave, component => { - if (!(component.GameObject is Player player)) return; + if (component.GameObject is not Player player) return; if (!playersInPhysicsObject.Contains(player)) return; playersInPhysicsObject.Remove(player); }); diff --git a/Uchu.World/Objects/Zone.cs b/Uchu.World/Objects/Zone.cs index f1a072f4..cf88fa43 100644 --- a/Uchu.World/Objects/Zone.cs +++ b/Uchu.World/Objects/Zone.cs @@ -107,6 +107,9 @@ public class Zone : Object //appears to actually do nothing? "race_imagine_powerup.lua", + + //already implemented by checking whenever an object is smashed + "race_smash_server.lua", }; public Zone(ZoneInfo zoneInfo, WorldUchuServer server, ushort instanceId = default, uint cloneId = default) From ad3148de71566fe37126e88849a09e316642fe31 Mon Sep 17 00:00:00 2001 From: frozenreflex Date: Sun, 20 Feb 2022 21:07:41 -0600 Subject: [PATCH 61/71] Add dragon animation and dragon egg manager scripts --- .../General/ImaginePowerupSpawner.cs | 2 +- .../General/SpawnPowerupsOnTimerThenDie.cs | 3 +- .../Racing/ForbiddenValleyDragonLap.cs | 68 ++++++++++++++++++ .../Racing/ForbiddenValleyEggManager.cs | 33 +++++++++ .../Racing/ForbiddenValleyEggs.cs | 1 + .../RacingControlComponent.cs | 69 ++++++++++--------- Uchu.World/Objects/Zone.cs | 2 +- 7 files changed, 142 insertions(+), 36 deletions(-) create mode 100644 Uchu.StandardScripts/Racing/ForbiddenValleyDragonLap.cs create mode 100644 Uchu.StandardScripts/Racing/ForbiddenValleyEggManager.cs diff --git a/Uchu.StandardScripts/General/ImaginePowerupSpawner.cs b/Uchu.StandardScripts/General/ImaginePowerupSpawner.cs index d5eb265b..9d81249b 100644 --- a/Uchu.StandardScripts/General/ImaginePowerupSpawner.cs +++ b/Uchu.StandardScripts/General/ImaginePowerupSpawner.cs @@ -32,7 +32,7 @@ public ImaginePowerupSpawner(GameObject gameObject) : base(gameObject) Listen(physics.OnEnter, other => { // Return if the collided object isn't a player. - if (!(other.GameObject is Player player)) return; + if (other.GameObject is not Player player) return; if (!gameObject.GetComponent().Alive) return; // Play the effect, add the imagination, and destroy the power-up. diff --git a/Uchu.StandardScripts/General/SpawnPowerupsOnTimerThenDie.cs b/Uchu.StandardScripts/General/SpawnPowerupsOnTimerThenDie.cs index 7dca4bca..1ce77b79 100644 --- a/Uchu.StandardScripts/General/SpawnPowerupsOnTimerThenDie.cs +++ b/Uchu.StandardScripts/General/SpawnPowerupsOnTimerThenDie.cs @@ -31,7 +31,8 @@ protected async void SpawnPowerups(GameObject gameObject, int numCycles, float s await Task.Delay((int) (secPerCycle * 1000)); } await Task.Delay((int) (deathDelay * 1000)); - gameObject.GetComponent().SmashAsync(gameObject); + Object.Destroy(gameObject); + //gameObject.GetComponent().SmashAsync(gameObject); } } } \ No newline at end of file diff --git a/Uchu.StandardScripts/Racing/ForbiddenValleyDragonLap.cs b/Uchu.StandardScripts/Racing/ForbiddenValleyDragonLap.cs new file mode 100644 index 00000000..34dc2890 --- /dev/null +++ b/Uchu.StandardScripts/Racing/ForbiddenValleyDragonLap.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Uchu.Core; +using Uchu.World; +using Uchu.World.Scripting.Native; + +namespace Uchu.StandardScripts.Racing; + +public class ForbiddenValleyDragonLap : ObjectScript +{ + protected uint _lap; + private GameObject _dragon; + protected bool lap3Hack; + public ForbiddenValleyDragonLap(GameObject gameObject) : base(gameObject) + { + Task.Run(async () => + { + //???????? + await Task.Delay(7000); + _dragon = Zone.GameObjects.First(t => t.GetGroups().Contains("dragon")); + if (lap3Hack) return; + Listen(gameObject.GetComponent().OnEnter, other => + { + //i'm not colliding with the trigger for some reason, this never gets logged + //however, a dragon animation (mama dragon flies forward while spewing fire) is still being played + //could this be another bug with the lap3 script? all of them seem to have the wrong laps being checked + Logger.Log($"Something entered: {other.GameObject.GetType()}, {other.GameObject.Name}"); + if (other.GameObject is not Player player || + !Zone.ZoneControlObject.TryGetComponent(out var racingControlComponent)) return; + var lap = racingControlComponent.Players.First(i => i.Player == player).Lap; + if (lap != _lap - 1) return; + _dragon.Animate($"lap_0{_lap}"); + }); + }); + } +} + +[ScriptName("fv_race_dragon_lap1_server.lua")] +public class ForbiddenValleyDragonLap1 : ForbiddenValleyDragonLap +{ + public ForbiddenValleyDragonLap1(GameObject gameObject) : base(gameObject) + { + _lap = 1; + } +} + +[ScriptName("fv_race_dragon_lap2_server.lua")] +public class ForbiddenValleyDragonLap2 : ForbiddenValleyDragonLap +{ + public ForbiddenValleyDragonLap2(GameObject gameObject) : base(gameObject) + { + _lap = 2; + } +} + +//bugfix: lap3 is accidentally set to be a client script, this should load it anyway +[LotSpecific(9613)] +//[ScriptName("fv_race_dragon_lap3_server.lua")] +public class ForbiddenValleyDragonLap3 : ForbiddenValleyDragonLap +{ + public ForbiddenValleyDragonLap3(GameObject gameObject) : base(gameObject) + { + _lap = 3; + //Id specific? + lap3Hack = gameObject.Id != 2483696; + } +} \ No newline at end of file diff --git a/Uchu.StandardScripts/Racing/ForbiddenValleyEggManager.cs b/Uchu.StandardScripts/Racing/ForbiddenValleyEggManager.cs new file mode 100644 index 00000000..948c1302 --- /dev/null +++ b/Uchu.StandardScripts/Racing/ForbiddenValleyEggManager.cs @@ -0,0 +1,33 @@ +using System.Linq; +using System.Threading.Tasks; +using Uchu.World; +using Uchu.World.Scripting.Native; + +namespace Uchu.StandardScripts.Racing; + +[ZoneSpecific(1403)] +public class ForbiddenValleyEggManager : NativeScript +{ + public override Task LoadAsync() + { + Listen(Zone.OnStart, () => + { + if (!Zone.ZoneControlObject.TryGetComponent(out var racingControlComponent)) return; + var eggs = Zone.GameObjects.Where(i => i.Lot == 11406 || i.Lot == 11405).ToList(); + Task.Run(async () => + { + while (racingControlComponent != null) + { + await Task.Delay(1000); + if (racingControlComponent.Players.Any(p => p.RespawnIndex is >= 10 and < 14)) continue; + foreach (var egg in eggs) + { + if (!egg.TryGetComponent(out var destructibleComponent) || destructibleComponent.Alive) return; + await destructibleComponent.ResurrectAsync(); + } + } + }); + }); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs b/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs index cc5dd462..891c8fae 100644 --- a/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs +++ b/Uchu.StandardScripts/Racing/ForbiddenValleyEggs.cs @@ -10,6 +10,7 @@ public ForbiddenValleyEggs(GameObject gameObject) : base(gameObject) { Listen(gameObject.GetComponent().OnSmashed, (killer, lootOwner) => { + PlayFXEffect("onHit", "onHit", 4102); if (killer is not Player player) return; diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 9d44678f..cbb44a29 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -27,9 +27,9 @@ public class RacingControlComponent : ScriptedActivityComponent { public override ComponentId Id => ComponentId.RacingControlComponent; - private List _players = new List(); + public List Players = new(); - private RaceInfo _raceInfo = new RaceInfo(); + private RaceInfo _raceInfo = new(); private RacingStatus _racingStatus = RacingStatus.None; @@ -43,7 +43,7 @@ public class RacingControlComponent : ScriptedActivityComponent public RacingControlComponent() { - _raceInfo.LapCount = 1; + _raceInfo.LapCount = 3; _raceInfo.PathName = "MainPath"; // MainPath Listen(OnStart, async () => { @@ -170,11 +170,11 @@ private async Task OnPlayerLoad(Player player) await missionInventoryComponent.RacingEnterWorld(this.GameObject.Zone.ZoneId); // Register player - this._players.Add(new RacingPlayerInfo + this.Players.Add(new RacingPlayerInfo { Player = player, PlayerLoaded = true, - PlayerIndex = (uint)_players.Count + 1, + PlayerIndex = (uint)Players.Count + 1, NoSmashOnReload = true, RaceTime = new Stopwatch(), LapTime = new Stopwatch(), @@ -200,10 +200,10 @@ private async Task OnPlayerLoad(Player player) /// private async Task SendPlayerToMainWorldAsync(Player player) { - _players.RemoveAll(info => info.Player == player); + Players.RemoveAll(info => info.Player == player); await player.SendToWorldAsync(_returnData.ZoneId, _returnData.Position, _returnData.Rotation); - if (_players.Count == 0) + if (Players.Count == 0) { await Task.Delay(10000); await this.GameObject.UchuServer.StopAsync(); @@ -224,12 +224,12 @@ private void LoadPlayerCar(Player player) var startRotation = waypoint.Rotation; var spacing = 15; - var positionOffset = startRotation.VectorMultiply(Vector3.UnitX) * _players.Count * spacing; + var positionOffset = startRotation.VectorMultiply(Vector3.UnitX) * Players.Count * spacing; startPosition += positionOffset + Vector3.UnitY * 3; // Create car player.Teleport(startPosition, startRotation); - GameObject car = GameObject.Instantiate(this.GameObject.Zone.ZoneControlObject, 8092, startPosition, startRotation); + var car = GameObject.Instantiate(this.GameObject.Zone.ZoneControlObject, 8092, startPosition, startRotation); // Setup imagination var destroyableComponent = car.GetComponent(); @@ -290,13 +290,13 @@ private void LoadPlayerCar(Player player) }); // Register car for player - for (var i = 0; i < _players.Count; i++) + for (var i = 0; i < Players.Count; i++) { - RacingPlayerInfo info = _players[i]; + RacingPlayerInfo info = Players[i]; if (info.Player == player) { info.Vehicle = car; - _players[i] = info; + Players[i] = info; } } } @@ -316,13 +316,13 @@ private void InitRace() minSpawner?.Activate(); minSpawner?.SpawnAll(); - if (_players.Count > 2) + if (Players.Count > 2) { medSpawner?.Activate(); medSpawner?.SpawnAll(); } - if (_players.Count > 4) + if (Players.Count > 4) { maxSpawner?.Activate(); maxSpawner?.SpawnAll(); @@ -345,16 +345,16 @@ private void InitRace() // Listen for players entering and leaving. var playersInPhysicsObject = new List(); var index = i; - this.Listen(physics.OnEnter, async component => + Listen(physics.OnEnter, async component => { - if (!(component.GameObject is Player player)) return; + if (component.GameObject is not Player player) return; if (playersInPhysicsObject.Contains(player)) return; playersInPhysicsObject.Add(player); // TODO: Check for players driving in the wrong direction - await this.PlayerReachedCheckpoint(player, index); + await PlayerReachedCheckpoint(player, index); }); - this.Listen(physics.OnLeave, component => + Listen(physics.OnLeave, component => { if (component.GameObject is not Player player) return; if (!playersInPhysicsObject.Contains(player)) return; @@ -384,7 +384,7 @@ private void StartRace() _racingStatus = RacingStatus.Started; // Go! - foreach (var info in _players) + foreach (var info in Players) { info.RaceTime.Start(); info.LapTime.Start(); @@ -414,8 +414,8 @@ public void OnAcknowledgePossession(Player player, GameObject vehicle) /// private void OnRacingPlayerInfoResetFinished(Player player) { - var playerInfoIndex = _players.FindIndex(x => x.Player == player); - var playerInfo = _players[playerInfoIndex]; + var playerInfoIndex = Players.FindIndex(x => x.Player == player); + var playerInfo = Players[playerInfoIndex]; var car = playerInfo.Vehicle; if (!playerInfo.NoSmashOnReload) @@ -448,7 +448,7 @@ private void OnRacingPlayerInfoResetFinished(Player player) } playerInfo.NoSmashOnReload = false; - _players[playerInfoIndex] = playerInfo; + Players[playerInfoIndex] = playerInfo; } /// @@ -457,7 +457,7 @@ private void OnRacingPlayerInfoResetFinished(Player player) /// public void OnPlayerRequestDie(Player player) { - var racingPlayer = _players.Find(info => info.Player == player); + var racingPlayer = Players.Find(info => info.Player == player); racingPlayer.SmashedTimes++; Logger.Information($"{player} requested death - respawning to {racingPlayer.RespawnPosition}"); @@ -493,13 +493,16 @@ public void OnPlayerRequestDie(Player player) private async Task PlayerReachedCheckpoint(Player player, int index) { var waypoint = _path.Waypoints[index]; - var playerInfoIndex = _players.FindIndex(x => x.Player == player); - var playerInfo = _players[playerInfoIndex]; + var playerInfoIndex = Players.FindIndex(x => x.Player == player); + var playerInfo = Players[playerInfoIndex]; // Only count up // TODO: reset player if (playerInfo.RespawnIndex > index && index != 0) return; + //is it possible to back up slightly then go across the finish line to skip laps? this seems like it would allow + //for an exploit like that + //there needs to be playerInfo.RespawnIndex = (uint)index; playerInfo.RespawnPosition = waypoint.Position; @@ -533,23 +536,23 @@ private async Task PlayerReachedCheckpoint(Player player, int index) if (playerInfo.Player.TryGetComponent(out MissionInventoryComponent missionInventoryComponent)) { await missionInventoryComponent.RaceFinishedAsync(Zone.ZoneId, playerInfo.Finished, - playerInfo.RaceTime.ElapsedMilliseconds, playerInfo.SmashedTimes, _players.Count); + playerInfo.RaceTime.ElapsedMilliseconds, playerInfo.SmashedTimes, Players.Count); } Logger.Information($"Race finished: {playerInfo.Player}, place {playerInfo.Finished}, time: {playerInfo.RaceTime.ElapsedMilliseconds}, smashed: {playerInfo.SmashedTimes}, best lap: {playerInfo.BestLapTime.TotalMilliseconds}"); this.UpdateLeaderboard(playerInfo); // Set player score for rewards - this.SetParameter(playerInfo.Player, 1, _players.Count * 10 + playerInfo.Finished); + this.SetParameter(playerInfo.Player, 1, Players.Count * 10 + playerInfo.Finished); - if (_rankCounter >= _players.Count) + if (_rankCounter >= Players.Count) { Logger.Information("Race ended"); } } } - _players[playerInfoIndex] = playerInfo; + Players[playerInfoIndex] = playerInfo; GameObject.Serialize(this.GameObject); Logger.Information($"Player reached checkpoint: {index}"); } @@ -661,7 +664,7 @@ public override void Serialize(BitWriter writer) packet.ExpectedPlayerCount = (ushort)this.Participants.Count; - packet.PreRacePlayerInfos = this._players.Select(info => new PreRacePlayerInfo + packet.PreRacePlayerInfos = this.Players.Select(info => new PreRacePlayerInfo { Player = info.Player, Vehicle = info.Vehicle, @@ -671,14 +674,14 @@ public override void Serialize(BitWriter writer) packet.RaceInfo = _raceInfo; - packet.DuringRacePlayerInfos = this._players.Select(info => new DuringRacePlayerInfo + packet.DuringRacePlayerInfos = this.Players.Select(info => new DuringRacePlayerInfo { Player = info.Player, BestLapTime = (float)info.BestLapTime.TotalSeconds, RaceTime = (float)info.RaceTime.Elapsed.TotalSeconds, }).ToArray(); - packet.PostRacePlayerInfos = this._players.Select(info => new PostRacePlayerInfo + packet.PostRacePlayerInfos = this.Players.Select(info => new PostRacePlayerInfo { Player = info.Player, CurrentRank = info.Finished @@ -701,7 +704,7 @@ private struct MainWorldReturnData public Quaternion Rotation { get; set; } } - struct RacingPlayerInfo + public struct RacingPlayerInfo { public Player Player { get; set; } public GameObject Vehicle { get; set; } diff --git a/Uchu.World/Objects/Zone.cs b/Uchu.World/Objects/Zone.cs index cf88fa43..6408c5bd 100644 --- a/Uchu.World/Objects/Zone.cs +++ b/Uchu.World/Objects/Zone.cs @@ -492,7 +492,7 @@ internal void RegisterObject(Object obj) ManagedObjects.Add(obj); - if (!(obj is GameObject gameObject)) return; + if (obj is not GameObject gameObject) return; if ((gameObject.Id.Flags & ObjectIdFlags.Spawned) != 0) { From 4297e6b1723e488dbdf3d2cd61679b662a2f47bd Mon Sep 17 00:00:00 2001 From: TecCheck Date: Mon, 21 Feb 2022 14:02:48 +0100 Subject: [PATCH 62/71] Clean up RacingControlComponent --- .../RacingControlComponent.cs | 74 ++++++++----------- 1 file changed, 30 insertions(+), 44 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index cbb44a29..b2a0b3fd 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -45,10 +45,7 @@ public RacingControlComponent() { _raceInfo.LapCount = 3; _raceInfo.PathName = "MainPath"; // MainPath - Listen(OnStart, async () => - { - await LoadAsync(); - }); + Listen(OnStart, async () => await LoadAsync()); } private async Task LoadAsync() @@ -108,7 +105,7 @@ private async Task LoadAsync() if (_path == default) throw new Exception("Path not found"); - Listen(this.GameObject.OnMessageBoxRespond, OnMessageBoxRespond); + Listen(GameObject.OnMessageBoxRespond, OnMessageBoxRespond); Listen(Zone.OnPlayerLoad, player => { @@ -157,7 +154,7 @@ private async Task OnMessageBoxRespond(Player player, MessageBoxRespondMessage m /// private async Task OnPlayerLoad(Player player) { - Logger.Information("Player loaded into racing"); + Logger.Information($"{player} loaded into racing"); // If race has already started return player to main world if (_racingStatus != RacingStatus.None) @@ -186,9 +183,7 @@ private async Task OnPlayerLoad(Player player) Listen(player.OnPositionUpdate, (position, rotation) => { if (position.Y < _deathPlaneHeight && _racingStatus == RacingStatus.Started) - { OnPlayerRequestDie(player); - } }); Zone.Schedule(InitRace, 10000); @@ -204,11 +199,7 @@ private async Task SendPlayerToMainWorldAsync(Player player) await player.SendToWorldAsync(_returnData.ZoneId, _returnData.Position, _returnData.Rotation); if (Players.Count == 0) - { - await Task.Delay(10000); - await this.GameObject.UchuServer.StopAsync(); - Logger.Debug("Closed Server"); - } + Zone.Schedule(async () => await this.GameObject.UchuServer.StopAsync(), 10000); } /// @@ -332,7 +323,7 @@ private void InitRace() // This is not the most efficient way to do this. // This checks the distance to every respawn point every physics tick, // but we only really need to check the next one (or nearest few). - for (var i = 0; i < _path.Waypoints.Length; i++) + for (uint i = 0; i < _path.Waypoints.Length; i++) { var proximityObject = GameObject.Instantiate(GameObject.Zone); var physics = proximityObject.AddComponent(); @@ -490,21 +481,20 @@ public void OnPlayerRequestDie(Player player) }, 2000); } - private async Task PlayerReachedCheckpoint(Player player, int index) + private async Task PlayerReachedCheckpoint(Player player, uint index) { var waypoint = _path.Waypoints[index]; var playerInfoIndex = Players.FindIndex(x => x.Player == player); var playerInfo = Players[playerInfoIndex]; // Only count up - // TODO: reset player - if (playerInfo.RespawnIndex > index && index != 0) + if (!IsCheckpointValid(playerInfo.RespawnIndex, index)) + { + // TODO: reset player return; - //is it possible to back up slightly then go across the finish line to skip laps? this seems like it would allow - //for an exploit like that - //there needs to be + } - playerInfo.RespawnIndex = (uint)index; + playerInfo.RespawnIndex = index; playerInfo.RespawnPosition = waypoint.Position; playerInfo.RespawnRotation = player.Transform.Rotation; @@ -513,8 +503,8 @@ private async Task PlayerReachedCheckpoint(Player player, int index) { var lapTime = (int)playerInfo.LapTime.ElapsedMilliseconds; playerInfo.LapTime.Restart(); - playerInfo.Lap++; + Logger.Information($"{playerInfo.Player} now in lap {playerInfo.Lap}"); // Set new best lap if applicable @@ -529,26 +519,17 @@ private async Task PlayerReachedCheckpoint(Player player, int index) // If player finished race if (playerInfo.Lap >= _raceInfo.LapCount) { - var raceTime = (int)playerInfo.RaceTime.ElapsedMilliseconds; playerInfo.RaceTime.Stop(); playerInfo.Finished = ++_rankCounter; + // Progress missions if (playerInfo.Player.TryGetComponent(out MissionInventoryComponent missionInventoryComponent)) - { - await missionInventoryComponent.RaceFinishedAsync(Zone.ZoneId, playerInfo.Finished, - playerInfo.RaceTime.ElapsedMilliseconds, playerInfo.SmashedTimes, Players.Count); - } + await missionInventoryComponent.RaceFinishedAsync(Zone.ZoneId, playerInfo.Finished, playerInfo.RaceTime.ElapsedMilliseconds, playerInfo.SmashedTimes, Players.Count); + // Set player score and leaderboard + SetParameter(playerInfo.Player, 1, Players.Count * 10 + playerInfo.Finished); + UpdateLeaderboard(playerInfo); Logger.Information($"Race finished: {playerInfo.Player}, place {playerInfo.Finished}, time: {playerInfo.RaceTime.ElapsedMilliseconds}, smashed: {playerInfo.SmashedTimes}, best lap: {playerInfo.BestLapTime.TotalMilliseconds}"); - this.UpdateLeaderboard(playerInfo); - - // Set player score for rewards - this.SetParameter(playerInfo.Player, 1, Players.Count * 10 + playerInfo.Finished); - - if (_rankCounter >= Players.Count) - { - Logger.Information("Race ended"); - } } } @@ -557,6 +538,19 @@ await missionInventoryComponent.RaceFinishedAsync(Zone.ZoneId, playerInfo.Finish Logger.Information($"Player reached checkpoint: {index}"); } + private bool IsCheckpointValid(uint oldIndex, uint newIndex) + { + // Only allow forward + if (newIndex > oldIndex) + return true; + + // Only go from last checkpoint to first + if (oldIndex == _path.Waypoints.Length - 1 && newIndex == 0) + return true; + + return false; + } + private void UpdateLeaderboard(RacingPlayerInfo playerInfo) { var player = playerInfo.Player; @@ -646,14 +640,6 @@ public override void Construct(BitWriter writer) // override to be able to use ScriptedActivityComponent as base public override void Serialize(BitWriter writer) { - for (var i = 0; i < this.Parameters.Count; i++) - { - var parameters = Parameters[i]; - parameters[0] = 1; - parameters[1] = 265f; // doesn't appear on client - parameters[2] = 87f; // doesn't appear on client - } - base.Serialize(writer); StructPacketParser.WritePacket(this.GetSerializePacket(), writer); } From e681714c8cd43d2806539b4c73b4e4a1ebedcf23 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Tue, 22 Feb 2022 12:22:27 +0100 Subject: [PATCH 63/71] Reset player when driving wrong way --- .../RacingControlComponent.cs | 62 ++++++++++++++++--- Uchu.World/Utilities/SimpleTimer.cs | 54 ++++++++++++++++ 2 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 Uchu.World/Utilities/SimpleTimer.cs diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index b2a0b3fd..928896c2 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -167,7 +167,7 @@ private async Task OnPlayerLoad(Player player) await missionInventoryComponent.RacingEnterWorld(this.GameObject.Zone.ZoneId); // Register player - this.Players.Add(new RacingPlayerInfo + RacingPlayerInfo info = new RacingPlayerInfo { Player = player, PlayerLoaded = true, @@ -176,7 +176,12 @@ private async Task OnPlayerLoad(Player player) RaceTime = new Stopwatch(), LapTime = new Stopwatch(), BestLapTime = default, - }); + ResetTimer = new SimpleTimer(6000), + }; + + Listen(info.ResetTimer.OnElapsed, () => ResetPlayer(player)); + + this.Players.Add(info); LoadPlayerCar(player); @@ -450,6 +455,7 @@ public void OnPlayerRequestDie(Player player) { var racingPlayer = Players.Find(info => info.Player == player); racingPlayer.SmashedTimes++; + racingPlayer.ResetTimer.Stop(); Logger.Information($"{player} requested death - respawning to {racingPlayer.RespawnPosition}"); @@ -483,6 +489,7 @@ public void OnPlayerRequestDie(Player player) private async Task PlayerReachedCheckpoint(Player player, uint index) { + Logger.Information($"Player reached checkpoint: {index}"); var waypoint = _path.Waypoints[index]; var playerInfoIndex = Players.FindIndex(x => x.Player == player); var playerInfo = Players[playerInfoIndex]; @@ -490,10 +497,21 @@ private async Task PlayerReachedCheckpoint(Player player, uint index) // Only count up if (!IsCheckpointValid(playerInfo.RespawnIndex, index)) { - // TODO: reset player + // Don't reset player after respawning + if (playerInfo.RespawnIndex == index) + { + playerInfo.ResetTimer.Stop(); + return; + } + + // Don't restart timer if it's already running + if (!playerInfo.ResetTimer.IsRunning) + playerInfo.ResetTimer.Start(); + return; } + playerInfo.ResetTimer.Stop(); playerInfo.RespawnIndex = index; playerInfo.RespawnPosition = waypoint.Position; playerInfo.RespawnRotation = player.Transform.Rotation; @@ -535,17 +553,46 @@ private async Task PlayerReachedCheckpoint(Player player, uint index) Players[playerInfoIndex] = playerInfo; GameObject.Serialize(this.GameObject); - Logger.Information($"Player reached checkpoint: {index}"); + } + + private void ResetPlayer(Player player) + { + Logger.Information("Reset Player"); + var racingPlayer = Players.Find(info => info.Player == player); + + if (racingPlayer.RespawnPosition == Vector3.Zero) + racingPlayer.RespawnPosition = _path.Waypoints.First().Position; + + player.Zone.ExcludingMessage(new VehicleRemovePassiveBoostAction + { + Associate = racingPlayer.Vehicle, + }, player); + + Zone.BroadcastMessage(new RacingSetPlayerResetInfoMessage + { + Associate = GameObject, + CurrentLap = (int)racingPlayer.Lap, + FurthestResetPlane = racingPlayer.RespawnIndex, + PlayerId = player, + RespawnPos = racingPlayer.RespawnPosition + Vector3.UnitY * 5, + UpcomingPlane = racingPlayer.RespawnIndex + 1, + }); + + Zone.BroadcastMessage(new RacingResetPlayerToLastResetMessage + { + Associate = GameObject, + PlayerId = player, + }); } private bool IsCheckpointValid(uint oldIndex, uint newIndex) { - // Only allow forward - if (newIndex > oldIndex) + // Only allow forward and only a small checkpoint skip + if (newIndex > oldIndex && (newIndex - oldIndex) < 3) return true; // Only go from last checkpoint to first - if (oldIndex == _path.Waypoints.Length - 1 && newIndex == 0) + if (newIndex == 0 && (_path.Waypoints.Length - oldIndex) < 3) return true; return false; @@ -709,6 +756,7 @@ public struct RacingPlayerInfo public bool NoSmashOnReload { get; set; } public bool CollectedRewards { get; set; } public Stopwatch RaceTime { get; set; } + public SimpleTimer ResetTimer { get; set; } }; } } diff --git a/Uchu.World/Utilities/SimpleTimer.cs b/Uchu.World/Utilities/SimpleTimer.cs new file mode 100644 index 00000000..b8302de3 --- /dev/null +++ b/Uchu.World/Utilities/SimpleTimer.cs @@ -0,0 +1,54 @@ +using System.Timers; + +namespace Uchu.World +{ + public class SimpleTimer + { + private double _time; + + private Timer _timer; + + public Event OnElapsed { get; } + + /// + /// A timer that counts down + /// + /// The time in ms that it should take to count down. + public SimpleTimer(double time) + { + _time = time; + + _timer = new Timer(time); + _timer.Stop(); + _timer.AutoReset = false; + _timer.Elapsed += Elapsed; + + OnElapsed = new Event(); + } + + /// + /// Starts the timer with the predefined time + /// + public void Start() + { + _timer.Interval = _time; + _timer.Start(); + } + + /// + /// Stops the timer + /// + public void Stop() + { + _timer.Stop(); + } + + public bool IsRunning => _timer.Enabled; + + private void Elapsed(object sender, ElapsedEventArgs elapsedEventArgs) + { + Stop(); + OnElapsed.Invoke(); + } + } +} \ No newline at end of file From c108db69f7f577ce2e41e08a121af34ce7c6ce8d Mon Sep 17 00:00:00 2001 From: Fro Zen <38363715+Frozenreflex@users.noreply.github.com> Date: Tue, 22 Feb 2022 10:23:15 -0600 Subject: [PATCH 64/71] Oops --- Uchu.StandardScripts/Racing/ForbiddenValleyEggManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Uchu.StandardScripts/Racing/ForbiddenValleyEggManager.cs b/Uchu.StandardScripts/Racing/ForbiddenValleyEggManager.cs index 948c1302..fc8665fc 100644 --- a/Uchu.StandardScripts/Racing/ForbiddenValleyEggManager.cs +++ b/Uchu.StandardScripts/Racing/ForbiddenValleyEggManager.cs @@ -22,7 +22,7 @@ public override Task LoadAsync() if (racingControlComponent.Players.Any(p => p.RespawnIndex is >= 10 and < 14)) continue; foreach (var egg in eggs) { - if (!egg.TryGetComponent(out var destructibleComponent) || destructibleComponent.Alive) return; + if (!egg.TryGetComponent(out var destructibleComponent) || destructibleComponent.Alive) continue; await destructibleComponent.ResurrectAsync(); } } @@ -30,4 +30,4 @@ public override Task LoadAsync() }); return Task.CompletedTask; } -} \ No newline at end of file +} From 361db469a7e53a53fda751a63bfd58828a66f5e7 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Wed, 23 Feb 2022 15:39:59 +0100 Subject: [PATCH 65/71] Don't accept any new players when race has started --- .../RacingControlComponent.cs | 23 ++++------- .../ScriptedActivityComponent.cs | 41 ++++++++++--------- Uchu.World/WorldUchuServer.cs | 7 +++- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 928896c2..001badfd 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -52,11 +52,12 @@ private async Task LoadAsync() { _path = Zone.ZoneInfo.LuzFile.PathData.FirstOrDefault(path => path.PathName == "MainPath"); + // In live this was done with zone specific scripts switch (Zone.ZoneId) { case 1203: _deathPlaneHeight = 100; - this.ActivityInfo = await ClientCache.FindAsync(42); + await SetupActivityInfo(42); _returnData = new MainWorldReturnData { ZoneId = 1200, @@ -66,7 +67,7 @@ private async Task LoadAsync() break; case 1303: _deathPlaneHeight = 0; - this.ActivityInfo = await ClientCache.FindAsync(39); + await SetupActivityInfo(39); _returnData = new MainWorldReturnData { ZoneId = 1300, @@ -76,7 +77,7 @@ private async Task LoadAsync() break; case 1403: _deathPlaneHeight = 300; - this.ActivityInfo = await ClientCache.FindAsync(54); + await SetupActivityInfo(54); _returnData = new MainWorldReturnData { ZoneId = 1400, @@ -95,13 +96,6 @@ private async Task LoadAsync() break; } - Rewards = ClientCache.FindAll(this.ActivityInfo.ActivityID).ToSortedList((a, b) => - { - if (a.ChallengeRating != b.ChallengeRating) - return (a.ChallengeRating ?? 1) - (b.ChallengeRating ?? 1); - return (a.ActivityRating ?? -1) - (b.ActivityRating ?? -1); - }).ToArray(); - if (_path == default) throw new Exception("Path not found"); @@ -303,6 +297,7 @@ private void InitRace() return; _racingStatus = RacingStatus.Loaded; + this.Zone.Server.SetMaxPlayerCount((uint)Players.Count); // Start imagination spawners var minSpawner = Zone.SpawnerNetworks.FirstOrDefault(gameObject => gameObject.Name == "ImaginationSpawn_Min"); @@ -587,12 +582,12 @@ private void ResetPlayer(Player player) private bool IsCheckpointValid(uint oldIndex, uint newIndex) { - // Only allow forward and only a small checkpoint skip - if (newIndex > oldIndex && (newIndex - oldIndex) < 3) + // Only allow forward (with a bit of tolerance) + if (newIndex > oldIndex && (newIndex - oldIndex) < 10) return true; - // Only go from last checkpoint to first - if (newIndex == 0 && (_path.Waypoints.Length - oldIndex) < 3) + // Only go from last checkpoint to first (with a bit of tolerance) + if (newIndex == 0 && (_path.Waypoints.Length - oldIndex) < 10) return true; return false; diff --git a/Uchu.World/Objects/Components/ReplicaComponents/ScriptedActivityComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/ScriptedActivityComponent.cs index 5102669f..472a5890 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/ScriptedActivityComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/ScriptedActivityComponent.cs @@ -33,27 +33,30 @@ protected ScriptedActivityComponent() Listen(OnStart, async () => { - if (!GameObject.Settings.TryGetValue("activityID", out var id)) - { - return; - } + if (GameObject.Settings.TryGetValue("activityID", out var id)) + await SetupActivityInfo((int)id); + }); + } - // Get the activity info. - var activityId = (int) id; - ActivityInfo = await ClientCache.FindAsync(activityId); - if (ActivityInfo == default) - { - Logger.Error($"{GameObject} has an invalid activityID: {activityId}"); - return; - } + protected async Task SetupActivityInfo(int activityId) + { + // Get the activity info. + ActivityInfo = await ClientCache.FindAsync(activityId); + if (ActivityInfo == default) + { + Logger.Error($"Invalid activityID: {activityId}"); + return; + } - // Get and sort the activities. - Rewards = ClientCache.FindAll(activityId).ToSortedList((a, b) => - { - if (a.ChallengeRating != b.ChallengeRating) return (a.ChallengeRating ?? 1) - (b.ChallengeRating ?? 1); - return (a.ActivityRating ?? -1) - (b.ActivityRating ?? -1); - }).ToArray(); - }); + // Get and sort the activities. + Rewards = ClientCache.FindAll(activityId).ToSortedList((a, b) => + { + if (a.ChallengeRating != b.ChallengeRating) return (a.ChallengeRating ?? 1) - (b.ChallengeRating ?? 1); + return (a.ActivityRating ?? -1) - (b.ActivityRating ?? -1); + }).ToArray(); + + if (ActivityInfo.MaxTeams != null && ActivityInfo.MaxTeamSize != null) + this.Zone.Server.SetMaxPlayerCount((uint)ActivityInfo.MaxTeams * (uint)ActivityInfo.MaxTeamSize); } /// diff --git a/Uchu.World/WorldUchuServer.cs b/Uchu.World/WorldUchuServer.cs index 026d2019..50b27cfc 100644 --- a/Uchu.World/WorldUchuServer.cs +++ b/Uchu.World/WorldUchuServer.cs @@ -27,7 +27,7 @@ public class WorldUchuServer : UchuServer public List Zones { get; } - public uint MaxPlayerCount { get; } + public uint MaxPlayerCount { get; private set; } public ZoneParser ZoneParser { get; private set; } @@ -51,6 +51,11 @@ public WorldUchuServer(Guid id) : base(id) _gameMessageHandlerMap = new GameMessageHandlerMap(); } + public void SetMaxPlayerCount(uint maxPlayerCount) + { + this.MaxPlayerCount = maxPlayerCount; + } + public override async Task ConfigureAsync(string configFile) { Logger.Information($"Created WorldServer on PID {Process.GetCurrentProcess().Id.ToString()}"); From d54e73ae400750bed11963510339b884169f7f20 Mon Sep 17 00:00:00 2001 From: frozenreflex Date: Wed, 2 Mar 2022 18:30:47 -0600 Subject: [PATCH 66/71] Add pillar animations, add RigidBodyPhantomPhysicsComponent, fix dragon animations Pillars don't collide yet, lap 3's dragon animation doesn't work yet --- .../Racing/ForbiddenValleyABCPillar.cs | 43 +++++++++++++++++++ .../Racing/ForbiddenValleyColumns.cs | 17 +++++++- .../Racing/ForbiddenValleyDPillar.cs | 36 ++++++++++++++++ .../Racing/ForbiddenValleyDragonLap.cs | 10 ++--- .../RacingControlComponent.cs | 5 ++- .../RigidBodyPhantomPhysicsComponent.cs | 15 +++++++ 6 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 Uchu.StandardScripts/Racing/ForbiddenValleyABCPillar.cs create mode 100644 Uchu.StandardScripts/Racing/ForbiddenValleyDPillar.cs diff --git a/Uchu.StandardScripts/Racing/ForbiddenValleyABCPillar.cs b/Uchu.StandardScripts/Racing/ForbiddenValleyABCPillar.cs new file mode 100644 index 00000000..70138831 --- /dev/null +++ b/Uchu.StandardScripts/Racing/ForbiddenValleyABCPillar.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Timers; +using Uchu.Core; +using Uchu.World; +using Uchu.World.Scripting.Native; + +namespace Uchu.StandardScripts.Racing; + +[ScriptName("fv_race_pillar_abc_server.lua")] +public class ForbiddenValleyABCPillar : ObjectScript +{ + private List _pillars; + private GameObject _dragon; + private bool _triggered; + public ForbiddenValleyABCPillar(GameObject gameObject) : base(gameObject) + { + Task.Run(async () => + { + await Task.Delay(7000); + _pillars = Zone.GameObjects.Where(t => t.GetGroups().Contains("pillars")).ToList(); + _dragon = Zone.GameObjects.First(t => t.GetGroups().Contains("dragon")); + if (!gameObject.TryGetComponent(out var physicsComponent) || + !Zone.ZoneControlObject.TryGetComponent(out var racingControlComponent)) return; + Listen(physicsComponent.OnEnter, other => + { + if (_triggered || racingControlComponent.Players.Find(i => i.Player == other.GameObject as Player).Lap != 1) return; + _triggered = true; + _pillars.First(i => i.Lot == 11946).Animate("crumble"); + _dragon.Animate("roar"); + AddTimerWithCancel(2.5f, "PillarBFall"); + AddTimerWithCancel(3.7f, "PillarCFall"); + }); + }); + } + public override void OnTimerDone(string timerName) + { + if (!timerName.StartsWith("Pillar")) return; + var lot = timerName[6] == 'B' ? 11947 : 11948; + _pillars.First(i => i.Lot == lot).Animate("crumble"); + } +} \ No newline at end of file diff --git a/Uchu.StandardScripts/Racing/ForbiddenValleyColumns.cs b/Uchu.StandardScripts/Racing/ForbiddenValleyColumns.cs index 040633c5..888f2e3a 100644 --- a/Uchu.StandardScripts/Racing/ForbiddenValleyColumns.cs +++ b/Uchu.StandardScripts/Racing/ForbiddenValleyColumns.cs @@ -1,3 +1,5 @@ +using System.Threading.Tasks; +using Uchu.Core; using Uchu.World; using Uchu.World.Scripting.Native; @@ -9,7 +11,20 @@ public class ForbiddenValleyColumns : ObjectScript public ForbiddenValleyColumns(GameObject gameObject) : base(gameObject) { var movingPlatformComponent = gameObject.GetComponent(); - + var lap = gameObject.Lot == 11306 ? 1: 2; + var activated = false; movingPlatformComponent.MoveTo(0); + Task.Run(async () => + { + await Task.Delay(7000); + if (!Zone.ZoneControlObject.TryGetComponent(out var racingControlComponent)) return; + Listen(racingControlComponent.OnPlayerLap, player => + { + if (activated || player.Lap < lap) return; + activated = true; + movingPlatformComponent.MoveTo(1); + Logger.Log("Platform moving"); + }); + }); } } diff --git a/Uchu.StandardScripts/Racing/ForbiddenValleyDPillar.cs b/Uchu.StandardScripts/Racing/ForbiddenValleyDPillar.cs new file mode 100644 index 00000000..1f2388a2 --- /dev/null +++ b/Uchu.StandardScripts/Racing/ForbiddenValleyDPillar.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Timers; +using Uchu.Core; +using Uchu.World; +using Uchu.World.Scripting.Native; + +namespace Uchu.StandardScripts.Racing; + +[ScriptName("fv_race_pillar_d_server.lua")] +public class ForbiddenValleyDPillar : ObjectScript +{ + private GameObject _pillar; + private GameObject _dragon; + private bool _triggered; + public ForbiddenValleyDPillar(GameObject gameObject) : base(gameObject) + { + Task.Run(async () => + { + await Task.Delay(7000); + _pillar = Zone.GameObjects.First(t => t.GetGroups().Contains("pillars") && t.Lot == 11949); + _dragon = Zone.GameObjects.First(t => t.GetGroups().Contains("dragon")); + if (!gameObject.TryGetComponent(out var physicsComponent) || + !Zone.ZoneControlObject.TryGetComponent(out var racingControlComponent)) return; + Listen(physicsComponent.OnEnter, other => + { + if (_triggered || racingControlComponent.Players.Find(i => i.Player == other.GameObject as Player).Lap != 2) return; + _triggered = true; + _pillar.Animate("crumble"); + _dragon.Animate("roar"); + Logger.Log("Pillar going down"); + }); + }); + } +} \ No newline at end of file diff --git a/Uchu.StandardScripts/Racing/ForbiddenValleyDragonLap.cs b/Uchu.StandardScripts/Racing/ForbiddenValleyDragonLap.cs index 34dc2890..52d02e71 100644 --- a/Uchu.StandardScripts/Racing/ForbiddenValleyDragonLap.cs +++ b/Uchu.StandardScripts/Racing/ForbiddenValleyDragonLap.cs @@ -12,6 +12,7 @@ public class ForbiddenValleyDragonLap : ObjectScript protected uint _lap; private GameObject _dragon; protected bool lap3Hack; + private bool activated = false; public ForbiddenValleyDragonLap(GameObject gameObject) : base(gameObject) { Task.Run(async () => @@ -21,15 +22,12 @@ public ForbiddenValleyDragonLap(GameObject gameObject) : base(gameObject) _dragon = Zone.GameObjects.First(t => t.GetGroups().Contains("dragon")); if (lap3Hack) return; Listen(gameObject.GetComponent().OnEnter, other => - { - //i'm not colliding with the trigger for some reason, this never gets logged - //however, a dragon animation (mama dragon flies forward while spewing fire) is still being played - //could this be another bug with the lap3 script? all of them seem to have the wrong laps being checked - Logger.Log($"Something entered: {other.GameObject.GetType()}, {other.GameObject.Name}"); + { if (other.GameObject is not Player player || !Zone.ZoneControlObject.TryGetComponent(out var racingControlComponent)) return; var lap = racingControlComponent.Players.First(i => i.Player == player).Lap; - if (lap != _lap - 1) return; + if (lap != _lap - 1 || activated) return; + activated = true; _dragon.Animate($"lap_0{_lap}"); }); }); diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 001badfd..0ea17272 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -28,6 +28,8 @@ public class RacingControlComponent : ScriptedActivityComponent public override ComponentId Id => ComponentId.RacingControlComponent; public List Players = new(); + + public Event OnPlayerLap { get; set; } private RaceInfo _raceInfo = new(); @@ -43,6 +45,7 @@ public class RacingControlComponent : ScriptedActivityComponent public RacingControlComponent() { + OnPlayerLap = new Event(); _raceInfo.LapCount = 3; _raceInfo.PathName = "MainPath"; // MainPath Listen(OnStart, async () => await LoadAsync()); @@ -517,7 +520,7 @@ private async Task PlayerReachedCheckpoint(Player player, uint index) var lapTime = (int)playerInfo.LapTime.ElapsedMilliseconds; playerInfo.LapTime.Restart(); playerInfo.Lap++; - + OnPlayerLap.Invoke(playerInfo); Logger.Information($"{playerInfo.Player} now in lap {playerInfo.Lap}"); // Set new best lap if applicable diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RigidBodyPhantomPhysicsComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RigidBodyPhantomPhysicsComponent.cs index 7f0c2ba6..ba030ef7 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RigidBodyPhantomPhysicsComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RigidBodyPhantomPhysicsComponent.cs @@ -1,8 +1,23 @@ +using Uchu.World.Client; + namespace Uchu.World { public class RigidBodyPhantomPhysicsComponent : StructReplicaComponent { public override ComponentId Id => ComponentId.RigidBodyPhantomPhysicsComponent; + protected RigidBodyPhantomPhysicsComponent() + { + Listen(OnStart, () => + { + // Find physics asset path from cdclient + var phantomPhysicsComponentId = GameObject.Lot.GetComponentId(ComponentId.RigidBodyPhantomPhysicsComponent); + var cdcComponent = ClientCache.Find(phantomPhysicsComponentId); + var assetPath = cdcComponent?.Physicsasset; + // Give physics object correct dimensions + var physicsComponent = GameObject.AddComponent(); + physicsComponent.SetPhysicsByPath(assetPath); + }); + } /// /// Creates the packet for the replica component. From cef66f1e1f875e80604d52f9a05a52028c915108 Mon Sep 17 00:00:00 2001 From: enteryournamehere <11255568+enteryournamehere@users.noreply.github.com> Date: Thu, 3 Mar 2022 22:47:48 +0100 Subject: [PATCH 67/71] Disable render distance filter for racing --- .../Components/ReplicaComponents/RacingControlComponent.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 0ea17272..cbadfd85 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -19,7 +19,7 @@ using Uchu.Core; using Uchu.Core.Client; using Uchu.Physics; -using Uchu.World.Client; +using Uchu.World.Filters; namespace Uchu.World { @@ -160,6 +160,9 @@ private async Task OnPlayerLoad(Player player) return; } + // Disable render distance filter + player.Perspective.GetFilter().Distance = 10000; + if (player.TryGetComponent(out var missionInventoryComponent)) await missionInventoryComponent.RacingEnterWorld(this.GameObject.Zone.ZoneId); @@ -473,7 +476,7 @@ public void OnPlayerRequestDie(Player player) CurrentLap = (int)racingPlayer.Lap, FurthestResetPlane = racingPlayer.RespawnIndex, PlayerId = player, - RespawnPos = racingPlayer.RespawnPosition + Vector3.UnitY * 5, + RespawnPos = racingPlayer.RespawnPosition + Vector3.UnitY * 3, UpcomingPlane = racingPlayer.RespawnIndex + 1, }); From 5cafb1d21b699e18bca4dc9c49ac5599ba0eec7d Mon Sep 17 00:00:00 2001 From: frozenreflex Date: Thu, 3 Mar 2022 23:51:43 -0600 Subject: [PATCH 68/71] Fix and add to script blacklist, make death triggers in racing only kill once, add water vehicle death trigger As far as I can tell, the water trigger is only used in the GF track, and I couldn't tell the difference, but it's there anyway. --- .../Racing/VehicleDeathTrigger.cs | 22 +++++++------ .../Racing/VehicleDeathTriggerWater.cs | 33 +++++++++++++++++++ .../RacingControlComponent.cs | 17 +++++++--- Uchu.World/Objects/Zone.cs | 22 ++++++++----- 4 files changed, 71 insertions(+), 23 deletions(-) create mode 100644 Uchu.StandardScripts/Racing/VehicleDeathTriggerWater.cs diff --git a/Uchu.StandardScripts/Racing/VehicleDeathTrigger.cs b/Uchu.StandardScripts/Racing/VehicleDeathTrigger.cs index f9e42a3d..f1e20a6a 100644 --- a/Uchu.StandardScripts/Racing/VehicleDeathTrigger.cs +++ b/Uchu.StandardScripts/Racing/VehicleDeathTrigger.cs @@ -12,18 +12,20 @@ public class VehicleDeathTrigger : ObjectScript /// public VehicleDeathTrigger(GameObject gameObject) : base(gameObject) { - if (!gameObject.TryGetComponent(out var physicsComponent)) - return; - - if (!gameObject.Zone.ZoneControlObject.TryGetComponent(out var racingControlComponent)) - return; - - + if (!gameObject.TryGetComponent(out var physicsComponent)) return; + if (!gameObject.Zone.ZoneControlObject.TryGetComponent(out var racingControlComponent)) return; Listen(physicsComponent.OnEnter, other => { - if (other.GameObject is not Player player) - return; - + if (other.GameObject is not Player player) return; + var car = player.GetComponent().VehicleObject; + Zone.BroadcastMessage(new DieMessage + { + Associate = car, + Killer = GameObject, + //KillType = KillType.Violent, + //ClientDeath = true, + SpawnLoot = false, + }); racingControlComponent.OnPlayerRequestDie(player); }); } diff --git a/Uchu.StandardScripts/Racing/VehicleDeathTriggerWater.cs b/Uchu.StandardScripts/Racing/VehicleDeathTriggerWater.cs new file mode 100644 index 00000000..b833458e --- /dev/null +++ b/Uchu.StandardScripts/Racing/VehicleDeathTriggerWater.cs @@ -0,0 +1,33 @@ +using Uchu.World; +using Uchu.World.Scripting.Native; + +namespace Uchu.StandardScripts.Racing; + +[ScriptName("vehicle_death_trigger_water_server.lua")] +public class VehicleDeathTriggerWater : ObjectScript +{ + /// + /// Script to kill cars that enter death triggers + /// + /// + public VehicleDeathTriggerWater(GameObject gameObject) : base(gameObject) + { + if (!gameObject.TryGetComponent(out var physicsComponent)) return; + if (!gameObject.Zone.ZoneControlObject.TryGetComponent(out var racingControlComponent)) return; + Listen(physicsComponent.OnEnter, other => + { + if (other.GameObject is not Player player) return; + var car = player.GetComponent().VehicleObject; + Zone.BroadcastMessage(new DieMessage + { + Associate = car, + Killer = GameObject, + KillType = KillType.Violent, + DeathType = "death_water", + ClientDeath = true, + SpawnLoot = false, + }); + racingControlComponent.OnPlayerRequestDie(player); + }); + } +} \ No newline at end of file diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index cbadfd85..ea49f689 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -164,7 +164,7 @@ private async Task OnPlayerLoad(Player player) player.Perspective.GetFilter().Distance = 10000; if (player.TryGetComponent(out var missionInventoryComponent)) - await missionInventoryComponent.RacingEnterWorld(this.GameObject.Zone.ZoneId); + await missionInventoryComponent.RacingEnterWorld(GameObject.Zone.ZoneId); // Register player RacingPlayerInfo info = new RacingPlayerInfo @@ -181,14 +181,23 @@ private async Task OnPlayerLoad(Player player) Listen(info.ResetTimer.OnElapsed, () => ResetPlayer(player)); - this.Players.Add(info); + Players.Add(info); LoadPlayerCar(player); Listen(player.OnPositionUpdate, (position, rotation) => { - if (position.Y < _deathPlaneHeight && _racingStatus == RacingStatus.Started) - OnPlayerRequestDie(player); + if (!(position.Y < _deathPlaneHeight) || _racingStatus != RacingStatus.Started) return; + var car = Players.First(i => i.Player == player).Vehicle; + Zone.BroadcastMessage(new DieMessage + { + Associate = car, + Killer = GameObject, + //KillType = KillType.Violent, + //ClientDeath = true, + SpawnLoot = false, + }); + OnPlayerRequestDie(player); }); Zone.Schedule(InitRace, 10000); diff --git a/Uchu.World/Objects/Zone.cs b/Uchu.World/Objects/Zone.cs index 6408c5bd..cb42a96a 100644 --- a/Uchu.World/Objects/Zone.cs +++ b/Uchu.World/Objects/Zone.cs @@ -94,7 +94,8 @@ public class Zone : Object public Event OnObject { get; } public Event OnTick { get; } public Event OnChatMessage { get; } - public static readonly string[] ScriptBlacklist = + + private static readonly string[] ScriptBlacklist = { //done with one script, comment out if needed "l_qb_spawner.lua", @@ -108,8 +109,14 @@ public class Zone : Object //appears to actually do nothing? "race_imagine_powerup.lua", - //already implemented by checking whenever an object is smashed + //implemented by checking whenever an object is smashed "race_smash_server.lua", + + //we may be missing parts of these scripts, so take note of what's inside of them, but most of the + //functionality is already implemented in the RacingControlComponent + "ns_race_server.lua", + "gf_race_server.lua", + "fv_race_server.lua", }; public Zone(ZoneInfo zoneInfo, WorldUchuServer server, ushort instanceId = default, uint cloneId = default) @@ -185,15 +192,12 @@ public void LoadScriptForObject(GameObject gameObject) { scriptNames.Add(scriptComponent.ScriptName.ToLower()); } - - foreach (var name in scriptNames) + + foreach (var script in ScriptBlacklist) { - for (var i = 0; i < ScriptBlacklist.Count(); i++) + foreach (var name in scriptNames.Where(i => i.EndsWith(script)).ToArray()) { - if (ScriptBlacklist[i].EndsWith(name)) - { - scriptNames.Remove(name); - } + scriptNames.Remove(name); } } From b812b7e72bf58a6db3148680da7c0c1bdd9c4b16 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Mon, 18 Jul 2022 12:17:13 +0200 Subject: [PATCH 69/71] Wait for players in minigame queue --- .../RacingControlComponent.cs | 142 ++++++++++++++---- Uchu.World/Systems/Match/MatchInstance.cs | 8 + 2 files changed, 124 insertions(+), 26 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index ea49f689..354e4c08 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -16,6 +16,8 @@ using InfectedRose.Luz; using Microsoft.Scripting.Utils; using RakDotNet.IO; +using Uchu.Api; +using Uchu.Api.Models; using Uchu.Core; using Uchu.Core.Client; using Uchu.Physics; @@ -53,6 +55,8 @@ public RacingControlComponent() private async Task LoadAsync() { + UchuServer.Api.RegisterCommandCollection(this); + _path = Zone.ZoneInfo.LuzFile.PathData.FirstOrDefault(path => path.PathName == "MainPath"); // In live this was done with zone specific scripts @@ -145,6 +149,51 @@ private async Task OnMessageBoxRespond(Player player, MessageBoxRespondMessage m } } + private void AddExpectedPlayer(ulong objectId) { + Logger.Information("PreLoad Player " + objectId); + + var fakePlayer = GameObject.Instantiate( + Zone, + position: Zone.SpawnPosition, + rotation: Zone.SpawnRotation, + scale: 1, + objectId: new ObjectId(objectId), + lot: 1 + ); + + if (!IsPlayerRegistered(fakePlayer)) { + Players.Add(new RacingPlayerInfo + { + Player = fakePlayer, + PlayerLoaded = false, + PlayerIndex = (uint)Players.Count + 1, + NoSmashOnReload = true, + RaceTime = new Stopwatch(), + LapTime = new Stopwatch(), + BestLapTime = default, + ResetTimer = new SimpleTimer(6000), + }); + } + } + + private bool IsPlayerRegistered(Player player) { + foreach (var info in Players) { + if (info.Player.Id == player.Id) + return true; + } + + return false; + } + + private bool AllRegisteredPlayersReady() { + foreach (var info in Players) { + if (!info.PlayerLoaded) + return false; + } + + return true; + } + /// /// This runs when the player loads into the world, it registers the player /// @@ -166,25 +215,31 @@ private async Task OnPlayerLoad(Player player) if (player.TryGetComponent(out var missionInventoryComponent)) await missionInventoryComponent.RacingEnterWorld(GameObject.Zone.ZoneId); - // Register player - RacingPlayerInfo info = new RacingPlayerInfo - { - Player = player, - PlayerLoaded = true, - PlayerIndex = (uint)Players.Count + 1, - NoSmashOnReload = true, - RaceTime = new Stopwatch(), - LapTime = new Stopwatch(), - BestLapTime = default, - ResetTimer = new SimpleTimer(6000), - }; - - Listen(info.ResetTimer.OnElapsed, () => ResetPlayer(player)); + RacingPlayerInfo playerInfo; - Players.Add(info); + if (IsPlayerRegistered(player)) { + playerInfo = Players.Find(info => info.Player.Id == player.Id); + playerInfo.Player = player; + playerInfo.PlayerLoaded = true; + } else { + playerInfo = new RacingPlayerInfo + { + Player = player, + PlayerLoaded = true, + PlayerIndex = (uint)Players.Count + 1, + NoSmashOnReload = true, + RaceTime = new Stopwatch(), + LapTime = new Stopwatch(), + BestLapTime = default, + ResetTimer = new SimpleTimer(6000), + }; + Players.Add(playerInfo); + } LoadPlayerCar(player); + Listen(playerInfo.ResetTimer.OnElapsed, () => ResetPlayer(player)); + Listen(player.OnPositionUpdate, (position, rotation) => { if (!(position.Y < _deathPlaneHeight) || _racingStatus != RacingStatus.Started) return; @@ -200,7 +255,16 @@ private async Task OnPlayerLoad(Player player) OnPlayerRequestDie(player); }); - Zone.Schedule(InitRace, 10000); + // Wait for players to join + // If all players from the queue are loaded wait + // another 10 seconds max + // NOTE: This code schedules 'InitRace' multiple times + // but 'InitRace' will only do it's thing once. There is + // room for improvement here... + if (AllRegisteredPlayersReady()) + Zone.Schedule(InitRace, 10000); + else + Zone.Schedule(InitRace, 30000); } /// @@ -294,16 +358,8 @@ private void LoadPlayerCar(Player player) VehicleId = car, }); - // Register car for player - for (var i = 0; i < Players.Count; i++) - { - RacingPlayerInfo info = Players[i]; - if (info.Player == player) - { - info.Vehicle = car; - Players[i] = info; - } - } + var playerInfo = Players.Find(info => info.Player.Id == player.Id); + playerInfo.Vehicle = car; } private void InitRace() @@ -311,6 +367,12 @@ private void InitRace() if (_racingStatus != RacingStatus.None) return; + // Remove all players that aren't loaded yet + for (var i = Players.Count - 1; i > 0; i--) { + if (!Players[i].PlayerLoaded) + Players.RemoveAt(i); + } + _racingStatus = RacingStatus.Loaded; this.Zone.Server.SetMaxPlayerCount((uint)Players.Count); @@ -421,6 +483,9 @@ public void OnAcknowledgePossession(Player player, GameObject vehicle) private void OnRacingPlayerInfoResetFinished(Player player) { var playerInfoIndex = Players.FindIndex(x => x.Player == player); + if (playerInfoIndex < 0 || playerInfoIndex >= Players.Count) + return; + var playerInfo = Players[playerInfoIndex]; var car = playerInfo.Vehicle; @@ -768,5 +833,30 @@ public struct RacingPlayerInfo public Stopwatch RaceTime { get; set; } public SimpleTimer ResetTimer { get; set; } }; + + public class RacingMatchCommands { + + RacingControlComponent RacingControl; + + public RacingMatchCommands(RacingControlComponent racingControl) { + this.RacingControl = racingControl; + } + + [ApiCommand("match/addMatchPlayer")] + public object AddMatchPlayer(string id) + { + var response = new BaseResponse(); + + if (ulong.TryParse(id, out var objectId)) + { + RacingControl.AddExpectedPlayer(objectId); + response.Success = true; + return response; + } + + response.FailedReason = "invalid id"; + return response; + } + } } } diff --git a/Uchu.World/Systems/Match/MatchInstance.cs b/Uchu.World/Systems/Match/MatchInstance.cs index 93484d0f..5adf5cb7 100644 --- a/Uchu.World/Systems/Match/MatchInstance.cs +++ b/Uchu.World/Systems/Match/MatchInstance.cs @@ -128,6 +128,14 @@ public MatchInstance(int type, Zone zone) zoneLoadedTimer.Stop(); zoneLoadedTimer.Dispose(); + // Tell server what players to expect + foreach (var player in _players) + { + var response = await zone.Server.Api.RunCommandAsync( + allocatedInstance.ApiPort, $"match/addMatchPlayer?id={player.Id}") + .ConfigureAwait(false); + } + // Start the match. foreach (var player in _players) { From 4830c839f7f7829b6d34997cfdbcd3da6dd08826 Mon Sep 17 00:00:00 2001 From: TecCheck Date: Thu, 28 Jul 2022 14:38:25 +0200 Subject: [PATCH 70/71] Handle players leaving --- .../RacingControlComponent.cs | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs index 354e4c08..12858cea 100644 --- a/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs +++ b/Uchu.World/Objects/Components/ReplicaComponents/RacingControlComponent.cs @@ -115,6 +115,8 @@ private async Task LoadAsync() Listen(player.OnRacingPlayerInfoResetFinished, () => OnRacingPlayerInfoResetFinished(player)); Listen(player.OnAcknowledgePossession, vehicle => OnAcknowledgePossession(player, vehicle)); }); + + Listen(Zone.OnPlayerLeave, player => RemoveRacingPlayer(player)); } /// @@ -209,6 +211,16 @@ private async Task OnPlayerLoad(Player player) return; } + // Set respawn point so when the players leave unexpectedly + // and relogin they don't spawn in a racing world + if (player.TryGetComponent(out var characterComponent)) + { + characterComponent.LastZone = _returnData.ZoneId; + characterComponent.SpawnPosition = _returnData.Position; + characterComponent.SpawnRotation = _returnData.Rotation; + } + await player.GetComponent().SaveAsync(); + // Disable render distance filter player.Perspective.GetFilter().Distance = 10000; @@ -273,11 +285,19 @@ private async Task OnPlayerLoad(Player player) /// private async Task SendPlayerToMainWorldAsync(Player player) { - Players.RemoveAll(info => info.Player == player); await player.SendToWorldAsync(_returnData.ZoneId, _returnData.Position, _returnData.Rotation); + RemoveRacingPlayer(player); + } + + private void RemoveRacingPlayer(Player player) + { + Players.RemoveAll(info => info.Player == player); if (Players.Count == 0) + { Zone.Schedule(async () => await this.GameObject.UchuServer.StopAsync(), 10000); + Logger.Information("No players left in racing world. Closing instance"); + } } /// @@ -483,8 +503,11 @@ public void OnAcknowledgePossession(Player player, GameObject vehicle) private void OnRacingPlayerInfoResetFinished(Player player) { var playerInfoIndex = Players.FindIndex(x => x.Player == player); - if (playerInfoIndex < 0 || playerInfoIndex >= Players.Count) + + if (playerInfoIndex < 0 || playerInfoIndex >= Players.Count) { + Logger.Warning("Invalid playerInfoIndex"); return; + } var playerInfo = Players[playerInfoIndex]; var car = playerInfo.Vehicle; From 9f50063e2568250d710194a6db8750597d2b66cb Mon Sep 17 00:00:00 2001 From: Teccheck Date: Tue, 18 Jul 2023 22:39:53 +0200 Subject: [PATCH 71/71] Revert min player count in racing task to 3 --- Uchu.World/Systems/Missions/RacingTask.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Uchu.World/Systems/Missions/RacingTask.cs b/Uchu.World/Systems/Missions/RacingTask.cs index e36eaa3c..36f91536 100644 --- a/Uchu.World/Systems/Missions/RacingTask.cs +++ b/Uchu.World/Systems/Missions/RacingTask.cs @@ -20,7 +20,7 @@ public async Task ReportRaceDone(ZoneId zoneId, uint place, long racetime, uint if (!Targets.Contains(zoneId)) return; - int minPlayerCount = 0; // TODO: 3; + int minPlayerCount = 3; switch ((RacingTaskType)Parameters[0]) { case RacingTaskType.Rank: