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

Fix/fixes 11/30 #647

Merged
merged 17 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
588d930
More tweaks to player tracking, with more verbose logging so if it st…
RyanYappert Nov 30, 2024
77a74b8
Slightly player-proof q26, A Servant's Pledge.
RyanYappert Nov 30, 2024
1f4b8a7
Guard against (harmless) null pointer issue when warping as part of a…
RyanYappert Dec 1, 2024
b769148
More verbose and better handling for CraftStartCraftHandler when a du…
RyanYappert Dec 1, 2024
931c26e
More guards against missing quests; this may be the quest board issue…
RyanYappert Dec 1, 2024
8298fd5
Fixing missing constructor in S2CChatSendTellMsgRes that was causing …
RyanYappert Dec 1, 2024
5a4ad2a
Fix EnqueueToClan null reference issue.
RyanYappert Dec 1, 2024
b14d7e6
Probably meaningless guard in ProfileGetCharacterProfileHandler
RyanYappert Dec 1, 2024
f916151
Ensure that an ordered quest properly exists in QuestQuestOrderHandler
RyanYappert Dec 1, 2024
183ee05
Fix missing byte in S2CPawnExpeditionGetSallyInfoRes
RyanYappert Dec 2, 2024
95c7e56
Tweaks to some quest priority stuff; guards against leaderless parties.
RyanYappert Dec 3, 2024
d3efff2
Prevent double-membership for pawns in party, causing XP duplication.
RyanYappert Dec 3, 2024
4822ea1
More aggressive logging for Get/SetPriorityQuestHandler, per @pacampb…
RyanYappert Dec 4, 2024
98bd477
Add quest guards to `SharedQuestStateManager::UpdatePriorityQuestList`
RyanYappert Dec 5, 2024
853f00d
Review feedback.
RyanYappert Dec 5, 2024
8eade15
Guards in BitterblackMazeManager::HandleTierClear. Update EntryBoardE…
RyanYappert Dec 6, 2024
b42d06a
Missing return in EquipManager::HandleChangeEquipList.
RyanYappert Dec 6, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,12 @@ public static PacketQueue HandleTierClear(DdonGameServer server, GameClient clie

var progress = character.BbmProgress;

var match = server.AssetRepository.BitterblackMazeAsset.Stages.Select(x => x.Value).Where(x => x.ContentId == progress.ContentId).FirstOrDefault();
var match = server.AssetRepository.BitterblackMazeAsset.Stages.Select(x => x.Value).Where(x => x.ContentId == progress.ContentId).FirstOrDefault()
?? throw new ResponseErrorException(ErrorCode.ERROR_CODE_FAIL); // TODO: Better error code.

var stageProgression = server.AssetRepository.BitterblackMazeAsset.StageProgressionList.Where(x => x.Id == match.ContentId).FirstOrDefault()
?? throw new ResponseErrorException(ErrorCode.ERROR_CODE_FAIL); // TODO: Better error code.

var stageProgression = server.AssetRepository.BitterblackMazeAsset.StageProgressionList.Where(x => x.Id == match.ContentId).FirstOrDefault();
if (stageProgression.ConnectionList.Count > 0)
{
var clearNtc = new S2CBattleContentClearTierNtc()
Expand Down
2 changes: 1 addition & 1 deletion Arrowgene.Ddon.GameServer/Characters/ClanManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,7 @@ public void EnqueueToClan<T>(uint clanId, T res, PacketQueue queue)
StructurePacket<T> packet = new StructurePacket<T>(res);
foreach (GameClient client in Server.ClientLookup.GetAll())
{
if (client.Character.ClanId == clanId)
if (client.Character is not null && client.Character.ClanId == clanId)
{
queue.Enqueue((client, packet));
}
Expand Down
2 changes: 1 addition & 1 deletion Arrowgene.Ddon.GameServer/Characters/EpitaphRoadManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,7 @@ public void AreaChange(GameClient client, uint stageId, PacketQueue queue)

lock (_TrialsInProgress)
{
if (TrialInProgress(client.Party) && client.Party.Leader.Client == client)
if (TrialInProgress(client.Party) && client.Party.Leader?.Client == client)
{
EndTrial(client.Party, GetPartyState(client.Party), SoulOrdealEndState.Failed);
}
Expand Down
2 changes: 2 additions & 0 deletions Arrowgene.Ddon.GameServer/Characters/EquipManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ public PacketQueue HandleChangeEquipList(DdonGameServer server, GameClient clien

client.Enqueue(updateCharacterItemNtc, queue);
server.ClientLookup.EnqueueToAll(changePawnEquipNtc, queue);

return queue;
}

throw new ResponseErrorException(ErrorCode.ERROR_CODE_FAIL); //TODO: Find a better code.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public BattleContentPartyMemberInfoHandler(DdonGameServer server) : base(server)

public override S2CBattleContentPartyMemberInfoRes Handle(GameClient client, C2SBattleContentPartyMemberInfoReq request)
{
var leader = client.Party.Leader.Client.Character;
var leader = client.Party.Leader?.Client.Character
?? throw new ResponseErrorException(ErrorCode.ERROR_CODE_PARTY_LEADER_ABSENCE);

bool contentSynced = true;
foreach (var memberClient in client.Party.Clients)
Expand Down
13 changes: 12 additions & 1 deletion Arrowgene.Ddon.GameServer/Handler/CraftStartCraftHandler.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Arrowgene.Ddon.GameServer.Characters;
Expand Down Expand Up @@ -36,9 +37,19 @@ public CraftStartCraftHandler(DdonGameServer server) : base(server)

public override S2CCraftStartCraftRes Handle(GameClient client, C2SCraftStartCraftReq request)
{
CDataMDataCraftRecipe recipe = Server.AssetRepository.CraftingRecipesAsset
CDataMDataCraftRecipe recipe;
try
{
recipe = Server.AssetRepository.CraftingRecipesAsset
.SelectMany(recipes => recipes.RecipeList)
.Single(recipe => recipe.RecipeID == request.RecipeID);
}
catch (InvalidOperationException)
{
Logger.Error($"Duplicate recipe ID {request.RecipeID}!!!");
throw new ResponseErrorException(ErrorCode.ERROR_CODE_CRAFT_RECIPE_NOT_FOUND, $"Duplicate recipe ID {request.RecipeID}");
}

ClientItemInfo itemInfo = ClientItemInfo.GetInfoForItemId(Server.AssetRepository.ClientItemInfos, recipe.ItemID);


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace Arrowgene.Ddon.GameServer.Handler
{
public class EntryBoardEntryBoardItemEntryHandler : StructurePacketHandler<GameClient, C2SEntryBoardEntryBoardItemEntryReq>
public class EntryBoardEntryBoardItemEntryHandler : GameRequestPacketQueueHandler<C2SEntryBoardEntryBoardItemEntryReq, S2CEntryBoardEntryBoardItemEntryRes>
{
private static readonly ServerLogger Logger = LogProvider.Logger<ServerLogger>(typeof(EntryBoardEntryBoardItemEntryHandler));

Expand All @@ -22,33 +22,20 @@ public EntryBoardEntryBoardItemEntryHandler(DdonGameServer server) : base(server
_Server = server;
}

public override void Handle(GameClient client, StructurePacket<C2SEntryBoardEntryBoardItemEntryReq> request)
public override PacketQueue Handle(GameClient client, C2SEntryBoardEntryBoardItemEntryReq request)
{
var data = _Server.BoardManager.GetGroupData(request.Structure.EntryId);
if (data == null)
{
client.Send(new S2CEntryBoardEntryBoardItemEntryRes()
{
Error = (uint)ErrorCode.ERROR_CODE_ENTRY_BOARD_NO_ITEM
});
return;
}
PacketQueue queue = new();
var data = _Server.BoardManager.GetGroupData(request.EntryId)
?? throw new ResponseErrorException(ErrorCode.ERROR_CODE_ENTRY_BOARD_NO_ITEM);

if (data.ContentStatus != ContentStatus.Recruiting)
{
client.Send(new S2CEntryBoardEntryBoardItemEntryRes()
{
Error = (uint)ErrorCode.ERROR_CODE_ENTRY_BOARD_ITEM_CREATE_OVER
});
return;
throw new ResponseErrorException(ErrorCode.ERROR_CODE_ENTRY_BOARD_ITEM_CREATE_OVER);
}

if (!_Server.BoardManager.AddCharacterToGroup(data.EntryItem.Id, client.Character))
{
client.Send(new S2CEntryBoardEntryBoardItemEntryRes()
{
Error = (uint)ErrorCode.ERROR_CODE_ENTRY_BOARD_NO_SPACE
});
throw new ResponseErrorException(ErrorCode.ERROR_CODE_ENTRY_BOARD_NO_SPACE);
}

CDataEntryMemberData memberData;
Expand All @@ -57,7 +44,7 @@ public override void Handle(GameClient client, StructurePacket<C2SEntryBoardEntr
memberData = data.EntryItem.EntryMemberList.First(x => x.EntryFlag == false);
memberData.EntryFlag = true;
GameStructure.CDataCharacterListElement(memberData.CharacterListElement, client.Character);
}
}

S2CEntryBoardEntryBoardItemChangeMemberNtc ntc = new S2CEntryBoardEntryBoardItemChangeMemberNtc()
{
Expand All @@ -67,18 +54,19 @@ public override void Handle(GameClient client, StructurePacket<C2SEntryBoardEntr

foreach (var characterId in data.Members)
{
var memberClient = _Server.ClientLookup.GetClientByCharacterId(characterId);
memberClient.Send(ntc);
var memberClient = _Server.ClientLookup.GetClientByCharacterId(characterId)
?? throw new ResponseErrorException(ErrorCode.ERROR_CODE_ENTRY_BOARD_NO_USER);
memberClient.Enqueue(ntc, queue);
}

_Server.CharacterManager.UpdateOnlineStatus(client, client.Character, OnlineStatus.EntryBoard);

data.EntryItem.TimeOut = (ushort)_Server.BoardManager.GetRecruitmentTimeLeft(data.EntryItem.Id);

client.Send(new S2CEntryBoardEntryBoardItemEntryRes()
client.Enqueue(new S2CEntryBoardEntryBoardItemEntryRes()
{
EntryItem = data.EntryItem
});
}, queue);

// Work after the response to the request was handeled
if (data.Members.Count == data.EntryItem.Param.MaxEntryNum)
Expand All @@ -88,16 +76,21 @@ public override void Handle(GameClient client, StructurePacket<C2SEntryBoardEntr
{
var memberClient = _Server.ClientLookup.GetClientByCharacterId(characterId);
// Allows the menu to transition
memberClient.Send(new S2CEntryBoardEntryBoardItemReadyNtc()
memberClient.Enqueue(new S2CEntryBoardEntryBoardItemReadyNtc()
{
MaxMember = data.EntryItem.Param.MaxEntryNum,
TimeOut = BoardManager.ENTRY_BOARD_READY_TIMEOUT,
});
memberClient.Send(new S2CEntryBoardItemTimeoutTimerNtc() { TimeOut = BoardManager.ENTRY_BOARD_READY_TIMEOUT });
}, queue);
memberClient.Enqueue(new S2CEntryBoardItemTimeoutTimerNtc()
{
TimeOut = BoardManager.ENTRY_BOARD_READY_TIMEOUT
}, queue);
}

_Server.BoardManager.StartReadyUpTimer(data.EntryItem.Id, BoardManager.ENTRY_BOARD_READY_TIMEOUT);
}

return queue;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ private void NotifyDisconnect(GameClient client)
Server.HubManager.LeaveAllHubs(client);
Server.CharacterManager.UpdateDatabaseOnExit(client.Character);
Server.PartyManager.CleanupOnExit(client);
Server.RpcManager.AnnouncePlayerList();
Server.RpcManager.AnnouncePlayerList(client.Character);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ public ProfileGetCharacterProfileHandler(DdonGameServer server) : base(server)

public override S2CProfileGetCharacterProfileRes Handle(GameClient client, C2SProfileGetCharacterProfileReq request)
{
Character targetCharacter = Server.ClientLookup.GetClientByCharacterId(request.CharacterId)?.Character
?? Server.Database.SelectCharacter(request.CharacterId)
GameClient targetClient = Server.ClientLookup.GetClientByCharacterId(request.CharacterId);
Character targetCharacter = targetClient is not null ? targetClient.Character : Server.Database.SelectCharacter(request.CharacterId)
?? throw new ResponseErrorException(ErrorCode.ERROR_CODE_CHARACTER_DATA_INVALID_CHARACTER_ID);

S2CCharacterGetCharacterStatusNtc ntc = new S2CCharacterGetCharacterStatusNtc();
Expand Down
2 changes: 1 addition & 1 deletion Arrowgene.Ddon.GameServer/Handler/QuestCancelHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public override S2CQuestQuestCancelRes Handle(GameClient client, C2SQuestQuestCa
}
else
{
if (client.Party.Leader.Client == client) //Only the leader should be able to inform the party quest state.
if (client.Party.Leader?.Client == client) //Only the leader should be able to inform the party quest state.
{
questStateManager.CancelQuest(quest.QuestScheduleId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ public override void Handle(GameClient client, IPacket packet)
foreach (var questScheduleId in activeQuests)
{
var quest = QuestManager.GetQuestByScheduleId(questScheduleId);
if (quest is null)
{
continue;
}
var questStateManager = QuestManager.GetQuestStateManager(client, quest);
var questState = questStateManager.GetQuestState(questScheduleId);
pcap.QuestOrderList.Add(quest.ToCDataQuestOrderList(questState.Step));
Expand Down
45 changes: 23 additions & 22 deletions Arrowgene.Ddon.GameServer/Handler/QuestGetPriorityQuestHandler.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
using Arrowgene.Buffers;
using Arrowgene.Ddon.GameServer.Characters;
using Arrowgene.Ddon.GameServer.Dump;
using Arrowgene.Ddon.GameServer.Quests;
using Arrowgene.Ddon.Server;
using Arrowgene.Ddon.Server.Network;
using Arrowgene.Ddon.Shared.Entity.PacketStructure;
using Arrowgene.Ddon.Shared.Entity.Structure;
using Arrowgene.Ddon.Shared.Network;
using Arrowgene.Ddon.Shared.Model;
using Arrowgene.Logging;
using YamlDotNet.Serialization;

namespace Arrowgene.Ddon.GameServer.Handler
{
public class QuestGetPriorityQuestHandler : PacketHandler<GameClient>
public class QuestGetPriorityQuestHandler : GameRequestPacketHandler<C2SQuestGetPriorityQuestReq, S2CQuestGetPriorityQuestRes>
{
private static readonly ServerLogger Logger = LogProvider.Logger<ServerLogger>(typeof(QuestGetPriorityQuestHandler));

Expand All @@ -21,46 +16,52 @@ public QuestGetPriorityQuestHandler(DdonGameServer server) : base(server)
{
}

public override PacketId Id => PacketId.C2S_QUEST_GET_PRIORITY_QUEST_REQ;

public override void Handle(GameClient client, IPacket packet)
public override S2CQuestGetPriorityQuestRes Handle(GameClient client, C2SQuestGetPriorityQuestReq request)
{
// client.Send(GameFull.Dump_144);
S2CQuestGetPriorityQuestRes res = new S2CQuestGetPriorityQuestRes();

var partyLeader = client.Party.Leader;

Character partyLeader = client.Party.Leader?.Client.Character ?? client.Character;
CDataPriorityQuestSetting setting = new CDataPriorityQuestSetting();
setting.CharacterId = partyLeader.Client.Character.CharacterId;
setting.CharacterId = partyLeader.CharacterId;

var priorityQuestScheduleIds = Server.Database.GetPriorityQuestScheduleIds(partyLeader.Client.Character.CommonId);
var priorityQuestScheduleIds = Server.Database.GetPriorityQuestScheduleIds(partyLeader.CommonId);
foreach (var questScheduleId in priorityQuestScheduleIds)
{
var quest = QuestManager.GetQuestByScheduleId(questScheduleId);
if (!QuestManager.IsQuestEnabled(questScheduleId))
{
Logger.Error(client, $"Priority quest for quest state which doesn't exist or is not enabled, schedule {questScheduleId}");
Server.Database.DeletePriorityQuest(client.Character.CommonId, questScheduleId);
continue;
}

var quest = QuestManager.GetQuestByScheduleId(questScheduleId);
if (quest == null)
{
Logger.Error(client, $"No quest object exists for ${questScheduleId}");
continue;
}

var questStateManager = QuestManager.GetQuestStateManager(client, quest);
var questState = questStateManager.GetQuestState(questScheduleId);
if (questStateManager == null)
{
Logger.Error(client, $"Unable to fetch the quest state manager for ${questScheduleId}");
continue;
}

var questState = questStateManager.GetQuestState(questScheduleId);
if (questState == null)
{
// Quest State should not be null, but don't crash
// Just delete the quest from priority list
Server.Database.DeletePriorityQuest(partyLeader.Client.Character.CommonId, questScheduleId);
Logger.Error(client, $"Priority quest for quest state which doesn't exist, schedule {questScheduleId}");
Logger.Error(client, $"Failed to find quest state for ${questScheduleId}");
continue;
}

setting.PriorityQuestList.Add(quest.ToCDataPriorityQuest(questState.Step));
setting.PriorityQuestList.Add(quest.ToCDataPriorityQuest(questState?.Step ?? 0));
}

res.PriorityQuestSettingsList.Add(setting);

client.Send(res);
return res;
}
}
}
10 changes: 5 additions & 5 deletions Arrowgene.Ddon.GameServer/Handler/QuestQuestOrderHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ public QuestQuestOrderHandler(DdonGameServer server) : base(server)
public override S2CQuestQuestOrderRes Handle(GameClient client, C2SQuestQuestOrderReq request)
{
var res = new S2CQuestQuestOrderRes();
var questScheduleId = request.QuestScheduleId;
var quest = QuestManager.GetQuestByScheduleId(questScheduleId);
var questStateManager = QuestManager.GetQuestStateManager(client, quest);
var quest = QuestManager.GetQuestByScheduleId(request.QuestScheduleId)
?? throw new ResponseErrorException(Shared.Model.ErrorCode.ERROR_CODE_QUEST_INTERNAL_ERROR);

if (questStateManager.GetActiveQuestScheduleIds().Contains(questScheduleId))
var questStateManager = QuestManager.GetQuestStateManager(client, quest);
if (questStateManager.GetActiveQuestScheduleIds().Contains(quest.QuestScheduleId))
{
return res;
}

questStateManager.AddNewQuest(QuestManager.GetQuestByScheduleId(questScheduleId), 0);
questStateManager.AddNewQuest(quest, 0);
res.QuestProcessStateList.AddRange(quest.ToCDataQuestList(0).QuestProcessStateList);

return res;
Expand Down
21 changes: 18 additions & 3 deletions Arrowgene.Ddon.GameServer/Handler/QuestSetPriorityQuestHandler.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using Arrowgene.Ddon.GameServer.Characters;
using Arrowgene.Ddon.Server;
using Arrowgene.Ddon.Shared.Entity.PacketStructure;
using Arrowgene.Ddon.Shared.Network;
using Arrowgene.Logging;

namespace Arrowgene.Ddon.GameServer.Handler
Expand Down Expand Up @@ -32,11 +31,27 @@ public override S2CQuestSetPriorityQuestRes Handle(GameClient client, C2SQuestSe
Server.Database.DeletePriorityQuest(client.Character.CommonId, questScheduleId);
continue;
}

var quest = QuestManager.GetQuestByScheduleId(questScheduleId);
if (quest == null)
{
Logger.Error(client, $"No quest object exists for ${questScheduleId}");
continue;
}

var questStateManager = QuestManager.GetQuestStateManager(client, quest);
if (questStateManager == null)
{
Logger.Error(client, $"Unable to fetch the quest state manager for ${questScheduleId}");
continue;
}

var questState = questStateManager.GetQuestState(questScheduleId);
Copy link
Collaborator

@pacampbell pacampbell Dec 4, 2024

Choose a reason for hiding this comment

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

Should the function that gets the quest state do all these checks instead? Looking at the fixes, it looks like we do the same steps in multiple handlers.

Copy link
Collaborator Author

@RyanYappert RyanYappert Dec 5, 2024

Choose a reason for hiding this comment

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

At some point you need to pass the check back to the handler; that means either throwing an error or returning a null, and I've spoken before about how much I dislike silently returning null to indicate a failure state. Should we throw a MissingQuestException or something and then catch it in the handler?

EDIT: I guess there's also the option of a wrapped object like how the PartyManager does it, but that honestly just seems like an awkward way of avoiding a legitimate try/catch.

ntc.PriorityQuestList.Add(quest.ToCDataPriorityQuest(questState.Step));
if (questState == null)
{
Logger.Error(client, $"Failed to find quest state for ${questScheduleId}");
continue;
}
ntc.PriorityQuestList.Add(quest.ToCDataPriorityQuest(questState?.Step ?? 0));
}

client.Party.SendToAll(ntc);
Expand Down
3 changes: 2 additions & 1 deletion Arrowgene.Ddon.GameServer/Handler/WarpPartyWarpHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public override void Handle(GameClient client, StructurePacket<C2SWarpPartyWarpR

// Check if its true that the party leader has teleported there
// and that the request was sent before the time ran out
TimeSpan warpTimeDifference = DateTime.UtcNow - client.Party.Leader.Client.LastWarpDateTime;
var lastWarpDateTime = client.Party.Leader?.Client.LastWarpDateTime ?? DateTime.MinValue;
TimeSpan warpTimeDifference = DateTime.UtcNow - lastWarpDateTime;
if(warpTimeDifference > TimeSpan.FromSeconds(60)) {
res.Result = 1; // I guess
}
Expand Down
Loading
Loading