Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync leviathans gameplay #2106

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

tornac1234
Copy link
Collaborator

@tornac1234 tornac1234 commented Jan 10, 2024

Creature sync:

  • Sync Reaper Leviathans
  • Sync Sea Dragons
  • Sync Sea Treaders

Creature behaviours:

  • Sync leviathans attacking cyclops
  • Sync leviathans attacking a player or vehicle (seamoth or exosuit)

Weapon sync:

  • Sync statis rifle
  • Sync torpedos (better than current implementation)

Other related issues:

  • Fix player vitals not showing a creative player's name
  • Fix seamoth not spawning module inventory
  • Adds an InfectedMixin and a LiveMixin on remote players to ensure they're potential targets

TODO when everything is done:
- [ ] Ensure crash fishes can target other players POSTPONED to another PR (will be short but required both this one and #2101 )

@tornac1234
Copy link
Collaborator Author

Would require some tests :)

@tornac1234 tornac1234 mentioned this pull request Jan 10, 2024
2 tasks
@tornac1234 tornac1234 added Area: player Related to player character actions Area: AI Related to state machines (as used by the fish) labels Jan 10, 2024
@tornac1234 tornac1234 changed the title Sync leviathans Sync leviathans gameplay Jan 18, 2024
@tornac1234
Copy link
Collaborator Author

tornac1234 commented Jan 18, 2024

There's still the following to be looked into (but most of the code is ready for review):

  • sometimes, move animation are chunky and you can see a small jump (FIXED)
  • when another player gets eaten by a leviathan, they broadcast the related cinematic which is played on other players so a black screen appears as if they got eaten themselves by a leviathan (but they didn't)

}

// Has a remote player inside
return targetObject.GetComponentInChildren<RemotePlayerIdentifier>(true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a more performant way to check if a remote player is inside a cyclops? Like checking subroot id and if it's a cyclops? Traversing the complete cyclops seems kinda heavy depending on the algorithm. If it deep-first-search maybe we should implement a breadth-first-search, but that should be benchmarked beforehand.

Copy link
Collaborator Author

@tornac1234 tornac1234 Feb 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two cases here:

  • Remote player is in Cyclops' root
  • Remote player is FAR in the hierarchy, parented to the driver seat

Current issue: remote player is never unparented from the driver seat even after leaving it (???) so this would need a fix in another PR I guess

Possible solutions (that would come in the same PR mentionned above):

  • have a MB (either MultiplayerCyclops or a new one) store in a list all players (local and remote) which are currently in the cyclops, and use this list instead of the components search
  • One loop in the first children layer, and one in the driver seat layer

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's only the two cases could we either check only all direct children of the cyclops and from the driver seat down?

float aggressiveToNoise = __instance.aggressiveToNoise.Value;

Resolve<IPacketSender>().Send(new AttackCyclopsTargetChanged(creatureId, targetId, aggressiveToNoise));
ErrorMessage.AddMessage($"[SEND] {__instance.gameObject.name} attacks {__instance.currentTarget.name}");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pinning debug message so it's not forgotten.

NitroxClient/GameLogic/AI.cs Outdated Show resolved Hide resolved
NitroxClient/GameLogic/BulletManager.cs Show resolved Hide resolved
NitroxPatcher/Patches/Dynamic/StasisSphere_OnHit_Patch.cs Outdated Show resolved Hide resolved
NitroxPatcher/Patches/Dynamic/Vehicle_TorpedoShot_Patch.cs Outdated Show resolved Hide resolved
@tornac1234
Copy link
Collaborator Author

The latest Sea Dragons sync code wasn't tested because it's really hard without #2113


namespace NitroxServer.Communication.Packets.Processors;

public class SeaDragonAttackTargetProcessor : TransmitIfCanSeePacketProcessor<SeaDragonAttackTarget>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be simplified using primary constructors

public class SeaDragonAttackTargetProcessor(
    PlayerManager playerManager,
    EntityRegistry entityRegistry
) : TransmitIfCanSeePacketProcessor<SeaDragonSwatAttack>(playerManager, entityRegistry)

I don't put comments on the others processors but same idea (SeaDragonSwatAttackProcessor, SeaDragonGrabExosuitProcessor, RangedAttackLastTargetUpdateProcessor, ...)

using NitroxServer.GameLogic;
using NitroxServer.GameLogic.Entities;

namespace NitroxServer.Communication.Packets.Processors;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could regroup the different processors related to creature sync under a same folder ?
Thoughts on it ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have many files that should better be moved inside theme named folders. I would say we move this project wide refactor after V1.8

/// <summary>
/// Enables exosuits's position sync when they're released from sea dragons
/// </summary>
public sealed partial class SeaDrargon_ReleaseExosuit_Patch : NitroxPatch, IDynamicPatch
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small typo Dragon

NitroxClient/GameLogic/RemotePlayer.cs Outdated Show resolved Hide resolved

public static void Prefix(RangedAttackLastTarget __instance)
{
if (!Resolve<AI>().IsCreatureWhitelisted(__instance.creature) ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if is really hard to read.It should be splitted imo. The game won't go slower for few more IL lines generated


public static void Prefix(RangedAttackLastTarget __instance)
{
if (!Resolve<AI>().IsCreatureWhitelisted(__instance.creature) ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

@Measurity Measurity added this to the 1.8 milestone Aug 9, 2024
Comment on lines +48 to +49
// TODO: Adapt this code when #1780 is merged
Utils.PlayEnvSound(seaDragonMeleeAttack.attackSound, collider.transform.position, 20f);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#1780 is now merged :D

lastTarget.SetTargetInternal(targetObject);
lastTarget.targetLocked = locked;

if (aggressiveWhenSeeTarget.sightedSound != null && !aggressiveWhenSeeTarget.sightedSound.GetIsPlaying())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing MB against null

torpedo.tr.rotation = rotation;
torpedo.OnHit(default);
torpedo.Deactivate();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is not heaving the component an expected case? Or should it be logged?

}

// It should be set to inactive automatically in Bullet.Awake
GameObject playerSphereClone = GameObject.Instantiate(stasisSpherePrefab);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it maybe make sense to name the game object accordingly with the playerid in it?

using NitroxServer.GameLogic;
using NitroxServer.GameLogic.Entities;

namespace NitroxServer.Communication.Packets.Processors;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have many files that should better be moved inside theme named folders. I would say we move this project wide refactor after V1.8

namespace NitroxPatcher.Patches.Dynamic;

/// <summary>
/// Prevents non simulating players from running locally <see cref="RangedAttackLastTarget.StartCasting"/>.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Prevents non simulating players from running locally <see cref="RangedAttackLastTarget.StartCasting"/>.
/// Prevents non simulating players from running locally <see cref="RangedAttackLastTarget.StartCharging"/>.

remotelyControlled.enabled = false;
}

if (__instance.TryGetNitroxId(out NitroxId seaDragonId) && exosuit.TryGetNitroxId(out NitroxId targetId))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check if the player has a lock on the SeaDragon?

Comment on lines +23 to +38
// In this method, players are priority targets. This will also account for the local player case
IEcoTarget ecoTarget = EcoRegionManager.main.FindNearestTarget(RemotePlayer.PLAYER_ECO_TARGET_TYPE, __instance.transform.position, __instance.isTargetValidFilter, __instance.maxSearchRings);
if (ecoTarget != null && __instance.IsTargetValid(ecoTarget.GetGameObject()))
{
__result = ecoTarget.GetGameObject();
return false;
}

// To redirect the call to base.GetAggressionTarget(), we ensure the if is skipped in the original method
float timeLastPlayerAttack = __instance.timeLastPlayerAttack;
__instance.timeLastPlayerAttack = Time.time;

__result = __instance.GetAggressionTarget();

__instance.timeLastPlayerAttack = timeLastPlayerAttack;
return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this now also target the local player? I don't know if the local player is included in RemotePlayer.PLAYER_ECO_TARGET_TYPE so maybe this comment is pointless.

Comment on lines +51 to +52
* BroadcastSeaDragonAttackRemotePlayer(this, target); <---- INSERTED LINE
* if (component4 != null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a little to early, no? Broadcasting before all checks are done if the exosuit can even be grabed

Comment on lines +31 to +39
return new CodeMatcher(instructions).End()
.InsertAndAdvance([
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldarg_1),
new CodeInstruction(OpCodes.Ldarg_2),
new CodeInstruction(OpCodes.Call, Reflect.Method(() => BroadcastSwatAttack(default, default, default)))
])
.InstructionEnumeration();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure this works? won't this add the instructions after the ret?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: AI Related to state machines (as used by the fish) Area: player Related to player character actions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants