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

Updates to Fellowship locking #3044

Merged
merged 9 commits into from
Jun 27, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 51 additions & 16 deletions Source/ACE.Server/Entity/Fellowship.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using ACE.Server.Managers;
using ACE.Server.Network.GameEvent.Events;
using ACE.Server.Network.GameMessages.Messages;
using ACE.Server.Network.Structure;
using ACE.Server.WorldObjects;

using log4net;
Expand Down Expand Up @@ -36,11 +37,10 @@ public class Fellowship
public bool IsLocked; // only set through emotes. if a fellowship is locked, new fellowship members cannot be added

public Dictionary<uint, WeakReference<Player>> FellowshipMembers;
public Dictionary<uint, WeakReference<Player>> LockedMembers;

// todo: fellows departed
// if fellowship locked, and one of the fellows disconnects and reconnects,
// they can rejoin the fellowship within a certain amount of time
public Dictionary<uint, int> DepartedMembers;

public Dictionary<string, FellowshipLockData> FellowshipLocks;

public QuestManager QuestManager;

Expand All @@ -65,7 +65,8 @@ public Fellowship(Player leader, string fellowshipName, bool shareXP)

QuestManager = new QuestManager(this);
IsLocked = false;
LockedMembers = new Dictionary<uint, WeakReference<Player>>();
DepartedMembers = new Dictionary<uint, int>();
FellowshipLocks = new Dictionary<string, FellowshipLockData>();
}

/// <summary>
Expand All @@ -76,10 +77,25 @@ public void AddFellowshipMember(Player inviter, Player newMember)
if (inviter == null || newMember == null)
return;

if (IsLocked && !LockedMembers.ContainsKey(newMember.Guid.Full))
if (IsLocked)
{
inviter.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(inviter.Session, WeenieErrorWithString.LockedFellowshipCannotRecruit_, newMember.Name));
return;

if (!DepartedMembers.TryGetValue(newMember.Guid.Full, out var timeDeparted))
{
inviter.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(inviter.Session, WeenieErrorWithString.LockedFellowshipCannotRecruit_, newMember.Name));
//newMember.SendWeenieError(WeenieError.LockedFellowshipCannotRecruitYou);
return;
}
else
{
var timeLimit = Time.GetDateTimeFromTimestamp(timeDeparted).AddSeconds(600);
if (DateTime.UtcNow > timeLimit)
{
inviter.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(inviter.Session, WeenieErrorWithString.LockedFellowshipCannotRecruit_, newMember.Name));
//newMember.SendWeenieError(WeenieError.LockedFellowshipCannotRecruitYou);
return;
}
}
}

if (FellowshipMembers.Count == MaxFellows)
Expand Down Expand Up @@ -270,6 +286,13 @@ public void QuitFellowship(Player player, bool disband)
// most likely is not hit due to client proactively sending AssignNewLeader msg

FellowshipMembers.Remove(player.Guid.Full);

if (IsLocked)
{
if (!DepartedMembers.TryAdd(player.Guid.Full, (int)Time.GetUnixTime()))
DepartedMembers[player.Guid.Full] = (int)Time.GetUnixTime();
}

player.Fellowship = null;
player.Session.Network.EnqueueSend(new GameEventFellowshipQuit(player.Session, player.Guid.Full));
player.Session.Network.EnqueueSend(new GameMessageSystemChat("You no longer have permission to loot anyone else's kills.", ChatMessageType.Broadcast));
Expand All @@ -291,6 +314,13 @@ public void QuitFellowship(Player player, bool disband)
else if (!disband)
{
FellowshipMembers.Remove(player.Guid.Full);

if (IsLocked)
{
if (!DepartedMembers.TryAdd(player.Guid.Full, (int)Time.GetUnixTime()))
DepartedMembers[player.Guid.Full] = (int)Time.GetUnixTime();
}

player.Session.Network.EnqueueSend(new GameEventFellowshipQuit(player.Session, player.Guid.Full));
var fellowshipMembers = GetFellowshipMembers();
foreach (var member in fellowshipMembers.Values)
Expand Down Expand Up @@ -343,30 +373,35 @@ public void UpdateOpenness(bool isOpen)
SendWeenieErrorWithStringAndUpdate(openness, FellowshipName);
}

public void UpdateLock(bool isLocked)
public void UpdateLock(bool isLocked, string lockName)
{
// Unlocking a fellowship is not possible without disbanding in retail worlds, so in all likelihood, this is only firing for fellowships being locked by emotemanager

IsLocked = isLocked;

if (string.IsNullOrWhiteSpace(lockName))
lockName = "Undefined";

if (isLocked)
{
Open = false;

SendBroadcastAndUpdate("Your fellowship is now locked. You may not recruit new members. If you leave the fellowship, you have 15 minutes to be recruited back into the fellowship.");
DepartedMembers.Clear();

foreach (var fellow in GetFellowshipMembers().Values)
{
LockedMembers.TryAdd(fellow.Guid.Full, new WeakReference<Player>(fellow));
}
if (!FellowshipLocks.TryAdd(lockName, new FellowshipLockData(Time.GetUnixTime())))
FellowshipLocks[lockName].UpdateTimestamp(Time.GetUnixTime());

SendBroadcastAndUpdate("Your fellowship is now locked. You may not recruit new members. If you leave the fellowship, you have 15 minutes to be recruited back into the fellowship.");
}
else
{
// Unlocking a fellowship is not possible without disbanding in retail worlds, so in all likelihood, this never occurs

SendBroadcastAndUpdate("Your fellowship is now unlocked.");
DepartedMembers.Clear();

FellowshipLocks.Remove(lockName);

LockedMembers.Clear();
SendBroadcastAndUpdate("Your fellowship is now unlocked.");
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;

using ACE.Server.Network.Structure;
using ACE.Server.WorldObjects;

namespace ACE.Server.Network.GameEvent.Events
Expand All @@ -12,17 +12,6 @@ public class GameEventFellowshipFullUpdate : GameEventMessage
public GameEventFellowshipFullUpdate(Session session)
: base(GameEventType.FellowshipFullUpdate, GameMessageGroup.UIQueue, session)
{
// This is a naive, bare-bones implementation of 0x02BE, FullFellowshipUpdate.
// 0x02BE is fairly complicated, so the following code is at least valuable as an example of a valid server response.

// todo: The current implementation has race conditions,
// and there are questions that must be answered before it can be fixed.
// We need to figure out who "owns" the fellowship data.
// Does everyone get a turn to read from and modify the fellowship data, and if so, how is this managed?

// Currently, creating and leaving a fellowship is supported.
// Any other fellowship function is not yet supported.

var fellowship = session.Player.Fellowship;

#region PackableHashTable of fellowship table - <ObjectID,Fellow>
Expand All @@ -48,9 +37,14 @@ public GameEventFellowshipFullUpdate(Session session)

Writer.Write(Convert.ToUInt32(fellowship.IsLocked));

// TODO PackableHashTable of fellows departed - fellowsDeparted -<ObjectID,int>
Writer.Write((uint)0x00200000);
Writer.Write((uint)0x00200000);
PackableHashTable.WriteHeader(Writer, fellowship.DepartedMembers.Count);
LtRipley36706 marked this conversation as resolved.
Show resolved Hide resolved
foreach (var kvp in fellowship.DepartedMembers)
LtRipley36706 marked this conversation as resolved.
Show resolved Hide resolved
{
Writer.Write(kvp.Key);
Writer.Write(kvp.Value);
}

Writer.Write(fellowship.FellowshipLocks);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This does not match https://acemulator.github.io/protocol/Messages/0x02BE-S2C.html, nor does it match any of the code in pdb for acclient.exe

See the relevant functions: UIQueueManager::ProcessNetBlobData -> CM_Fellowship::DispatchUI_FullUpdate -> Fellowship::UnPack
-> ClientFellowshipSystem::Handle_Fellowship__FullUpdate
, specifically Fellowship:UnPack

The fellowship data structure ends at DepartedMembers, according to the client processing for this packet

Is this a feature that was added after the client pdb?

Copy link
Member Author

@LtRipley36706 LtRipley36706 Jun 26, 2020

Choose a reason for hiding this comment

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

its not found in UnPack, but is sent across wire. Not sure where in the client, but aclogview decodes it

Copy link
Collaborator

Choose a reason for hiding this comment

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

"Is this a feature that was added after the client pdb?"

Any answer to this question?

The pdb client doesn't use this data after DepartedMembers at all. The only explanation would be if this locking data structure was added at the end of the game, after the pdb client.

These PRs also need an explanation of what the changes actually are, the reasoning for the changes, and repro steps to test them. What effect does this have on the client, and how can it be tested?

Copy link
Member Author

@LtRipley36706 LtRipley36706 Jun 27, 2020

Choose a reason for hiding this comment

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

Not sure where in the client, but aclogview decodes it

it was sent across the wire. it probably has no purpose to the client

Copy link
Collaborator

Choose a reason for hiding this comment

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

looking in the final client, sub_5BA630 is Fellowship::UnPack

it looks to have no additional logic, and matches the pdb verison... DepartedFellows is the last thing it uses

if this data is completely unused by anything, and has 0 effect on the client or anything in the game, what is the purpose of this code if it does nothing?

Copy link
Member Author

Choose a reason for hiding this comment

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

to fill out aclogview parse.. the data was there as Writer.Write((uint)0x00200000);

Copy link
Member Author

@LtRipley36706 LtRipley36706 Jun 27, 2020

Choose a reason for hiding this comment

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

from retail pcaps

}

public void WriteFellow(Player fellow)
Expand Down
53 changes: 53 additions & 0 deletions Source/ACE.Server/Network/Structure/FellowshipLockData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Collections.Generic;
using System.IO;

namespace ACE.Server.Network.Structure
{
public class FellowshipLockData
{
public uint Unknown_1;
public uint Unknown_2;
public uint Unknown_3;
public uint Timestamp;
public uint Sequence;

public FellowshipLockData() { }

public FellowshipLockData(double timestamp)
{
Unknown_1 = 0x0; // always 0 in pcaps
Unknown_2 = 0x0; // unknown?
Unknown_3 = 0x0; // unknown?
Timestamp = (uint)timestamp;
Sequence = 1;
}

public void UpdateTimestamp(double timestamp)
{
Timestamp = (uint)timestamp;
Sequence++;
}
}

public static class FellowshipLockDataExtensions
{
public static void Write(this BinaryWriter writer, FellowshipLockData fellowshipLockData)
{
writer.Write(fellowshipLockData.Unknown_1);
writer.Write(fellowshipLockData.Unknown_2);
writer.Write(fellowshipLockData.Unknown_3);
writer.Write(fellowshipLockData.Timestamp);
writer.Write(fellowshipLockData.Sequence);
}

public static void Write(this BinaryWriter writer, Dictionary<string, FellowshipLockData> fellowshipLocks)
{
PackableHashTable.WriteHeader(writer, fellowshipLocks.Count);
foreach (var kvp in fellowshipLocks)
{
writer.WriteString16L(kvp.Key);
writer.Write(kvp.Value);
}
}
}
}
2 changes: 1 addition & 1 deletion Source/ACE.Server/WorldObjects/Managers/EmoteManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,7 @@ public float ExecuteEmote(PropertiesEmote emoteSet, PropertiesEmoteAction emote,
case EmoteType.LockFellow:

if (player != null && player.Fellowship != null)
player.HandleActionFellowshipChangeLock(true);
player.HandleActionFellowshipChangeLock(true, emoteSet.Quest);

break;

Expand Down
4 changes: 2 additions & 2 deletions Source/ACE.Server/WorldObjects/Player_Fellowship.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ public void HandleActionFellowshipChangeOpenness(bool openness)
}
}

public void HandleActionFellowshipChangeLock(bool lockState)
public void HandleActionFellowshipChangeLock(bool lockState, string lockName)
{
if (Fellowship != null)
Fellowship.UpdateLock(lockState);
Fellowship.UpdateLock(lockState, lockName);
}

public void FellowshipQuit(bool disband)
Expand Down