add more handle protocol

This commit is contained in:
VDH
2026-03-11 16:05:54 +07:00
parent 18b32f6d5f
commit 230bb83e6f
10 changed files with 300 additions and 102 deletions
+3
View File
@@ -100,3 +100,6 @@ InitTestScene*.unity*
# Auto-generated scenes by play mode tests
/[Aa]ssets/[Ii]nit[Tt]est[Ss]cene*.unity*
.idea
# AI Context
claude.md
+2 -2
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d381b291f0b51a7d9440c9129ad3176acc9c4e8a8ade93205cd8c87875c5b5db
size 309973
oid sha256:37a9988776424c6f09366178d3b6f3a06cfeaebb9be7f1386f1da7f5b6d16e8d
size 311771
@@ -1524,3 +1524,4 @@ public enum GfxSkillValType
@@ -14,6 +14,7 @@ using System.Runtime.InteropServices;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using static Unity.Burst.Intrinsics.X86.Avx;
namespace PerfectWorld.Scripts.Managers
{
@@ -102,6 +103,8 @@ namespace PerfectWorld.Scripts.Managers
case EC_MsgDef.MSG_PM_PLAYEREXIT:
OnMsgPlayerExit(Msg);
break;
case EC_MsgDef.MSG_PM_PLAYEREXTSTATE: OnMsgPlayerExtState(Msg); break;
}
}
else
@@ -110,7 +113,55 @@ namespace PerfectWorld.Scripts.Managers
}
return true;
}
public bool OnMsgPlayerExtState(ECMSG Msg)
{
int cid = 0;
cmd_icon_state_notify cmd = new cmd_icon_state_notify();
if (Convert.ToInt32(Msg.dwParam2) == CommandID.UPDATE_EXT_STATE)
{
var pCmd = GPDataTypeHelper.FromBytes<cmd_update_ext_state>((byte[])Msg.dwParam1);
cid = pCmd.id;
}
else if (Convert.ToInt32(Msg.dwParam2) == CommandID.ICON_STATE_NOTIFY)
{
if (!cmd.Initialize((byte[])Msg.dwParam1))
{
return false;
}
cid = cmd.id;
}
else
{
return false;
}
CECHostPlayer host = GetHostPlayer();
if (host != null && cid == host.GetCharacterID())
{
host.ProcessMessage(Msg);
}
else
{
EC_ElsePlayer elsePlayer = SeekOutElsePlayer(cid);
if (elsePlayer != null)
{
elsePlayer.ProcessMessage(Msg);
}
if (Convert.ToInt32(Msg.dwParam2) == CommandID.ICON_STATE_NOTIFY && host != null && host.GetTeam() != null)
{
// TODO: Update host's team member's icon state
// CECTeam pTeam = host.GetTeam();
// CECTeamMember pMember = pTeam.GetMemberByID(cid);
// if (pMember != null)
// pMember.ResetIconStates(cmd.states);
}
}
return true;
}
private void OnMsgPlayerDied(ECMSG Msg)
{
cmd_player_died pCmd = GPDataTypeHelper.FromBytes<cmd_player_died>((byte[])Msg.dwParam1);
@@ -813,7 +864,7 @@ namespace PerfectWorld.Scripts.Managers
float fDist = pPlayer.GetDistToHost();
if (fDist > EC_RoleTypes.EC_TABSEL_DIST || !pHost.CanSafelySelectWith(fDist))
continue; // Target is too far
continue; // Target is too far
aCands.Add(pPlayer);
}
@@ -96,6 +96,7 @@ namespace BrewMonster
public bool m_bPetInSanctuary = false; // true, the pet pet of the player is in sanctuary
//The ID of the currently summoned pet
protected int m_idCurPet = 0;
public List<IconState> m_aIconStates = new List<IconState>();
protected byte m_byPariahLvl = 0; // Pariah level
public RIDINGPET m_RidingPet; // Riding pet information
@@ -702,6 +703,45 @@ namespace BrewMonster
}
}
public virtual void OnMsgPlayerExtState(ECMSG Msg)
{
if (Convert.ToInt32(Msg.dwParam2) == CommandID.UPDATE_EXT_STATE)
{
cmd_update_ext_state pCmd = GPDataTypeHelper.FromBytes<cmd_update_ext_state>((byte[])Msg.dwParam1);
if (pCmd.id == m_PlayerInfo.cid)
{
SetNewExtendStates(0, pCmd.states, (int)OBJECT_EXT_STATE.OBJECT_EXT_STATE_COUNT);
}
}
else if (Convert.ToInt32(Msg.dwParam2) == CommandID.ICON_STATE_NOTIFY)
{
cmd_icon_state_notify cmd = new cmd_icon_state_notify();
if (!cmd.Initialize((byte[])Msg.dwParam1))
{
return;
}
if (cmd.id == m_PlayerInfo.cid)
{
m_aIconStates = cmd.states;
if (m_aIconStates.Count > 1)
{
m_aIconStates.Sort((a, b) =>
{
if (a.id < b.id) return -1;
if (a.id > b.id) return 1;
return 0;
});
}
}
}
}
public virtual void SetNewExtendStates(int unknown, uint[] states, int count)
{
// TODO: Implement appearance or physics change
}
public virtual void SetUpPlayer()
{
m_dwResFlags = 0;
@@ -221,7 +221,7 @@ namespace CSNetwork.GPDataType
public const int TEAM_ASK_TO_JOIN = 148;
public const int OBJECT_EMOTE_RESTORE = 149;
public const int CON_EMOTE_REQUEST = 150; // concurrent emote request
public const int CON_EMOTE_REQUEST = 150;
public const int DO_CONCURRENT_EMOTE = 151;
public const int MATTER_PICKUP = 152;
public const int MAFIA_INFO_NOTIFY = 153;
@@ -284,7 +284,7 @@ namespace CSNetwork.GPDataType
public const int EXIT_INSTANCE = 200;
public const int CHANGE_FACE_START = 201;
public const int CHANGE_FACE_END = 202;
public const int PLAYER_CHG_FACE = 203; // Player change face completed
public const int PLAYER_CHG_FACE = 203;
public const int OBJECT_CAST_POS_SKILL = 204;
public const int SET_MOVE_STAMP = 205;
@@ -403,7 +403,7 @@ namespace CSNetwork.GPDataType
public const int FACTION_RELATION_NOTIFY = 300;
public const int PLAYER_EQUIP_DISABLED = 301;
public const int PLAYER_SPEC_ITEM_LIST = 302; // return value of GM_QUERY_SPEC_ITEM
public const int PLAYER_SPEC_ITEM_LIST = 302;
public const int OBJECT_START_PLAY_ACTION = 303;
public const int OBJECT_STOP_PLAY_ACTION = 304;
@@ -2684,7 +2684,86 @@ namespace CSNetwork.GPDataType
public ushort equip_idx;
public uint cost;
};
public struct cmd_icon_state_notify
{
public int id;
public List<IconState> states;
public bool Initialize(byte[] buffer)
{
if (buffer == null || buffer.Length < 4) return false;
int offset = 0;
// Extract id
id = BitConverter.ToInt32(buffer, offset);
offset += sizeof(int);
// Extract scount
if (offset + sizeof(ushort) > buffer.Length) return false;
ushort scount = BitConverter.ToUInt16(buffer, offset);
offset += sizeof(ushort);
// Extract state array
ushort[] stateArray = new ushort[scount];
for (int i = 0; i < scount; i++)
{
if (offset + sizeof(ushort) > buffer.Length) return false;
stateArray[i] = BitConverter.ToUInt16(buffer, offset);
offset += sizeof(ushort);
}
// Extract pcount
if (offset + sizeof(ushort) > buffer.Length) return false;
ushort pcount = BitConverter.ToUInt16(buffer, offset);
offset += sizeof(ushort);
// Extract param array
int[] paramArray = new int[pcount];
for (int i = 0; i < pcount; i++)
{
if (offset + sizeof(int) > buffer.Length) return false;
paramArray[i] = BitConverter.ToInt32(buffer, offset);
offset += sizeof(int);
}
// Build states
states = new List<IconState>(scount);
int param_offset = 0;
for (int i = 0; i < scount; i++)
{
ushort s = stateArray[i];
IconState dummy = new IconState();
dummy.id = (ushort)(s & 0x3fff);
dummy.pcount = (s >> 14) & 0x3;
dummy.param = new int[3];
if (dummy.pcount > 0)
{
if (param_offset + dummy.pcount <= pcount)
{
for (int k = 0; k < dummy.pcount; k++)
{
dummy.param[k] = paramArray[param_offset++];
}
}
else
{
// Not enough parameters decoded
return false;
}
}
states.Add(dummy);
}
return true;
}
}
public struct IconState
{
public ushort id;
public int pcount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public int[] param;
};
// player leaves the world
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_player_leave_world
@@ -1283,6 +1283,15 @@ namespace CSNetwork
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SETMOVESTAMP, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
break;
case CommandID.UPDATE_EXT_STATE:
{
cmd_update_ext_state pCmd = GPDataTypeHelper.FromBytes<cmd_update_ext_state>((byte[])pDataBuf);
if (ISPLAYERID(pCmd.id))
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYEREXTSTATE, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader);
else if (ISNPCID(pCmd.id))
EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCEXTSTATE, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
break;
}
default:
#if UNITY_EDITOR
if (isDebug)
@@ -14,13 +14,13 @@ namespace BrewMonster
public partial class CECHostPlayer
{
// 服务器控制的额外操作限制
// 服务器控制的额外操作限制
public enum PLAYER_LIMIT
{
PLAYER_LIMIT_NOFLY, // 禁止"飞行/取消飞行"
PLAYER_LIMIT_NOCHANGESELECT, // 禁止"选中/取消选中/协助攻击"
PLAYER_LIMIT_NOMOUNT, // 禁止召唤骑宠
PLAYER_LIMIT_NOBIND, // 禁止"发起/接收相依相偎"
PLAYER_LIMIT_NOFLY, // 禁止"飞行/取消飞行"
PLAYER_LIMIT_NOCHANGESELECT, // 禁止"选中/取消选中/协助攻击"
PLAYER_LIMIT_NOMOUNT, // 禁止召唤骑宠
PLAYER_LIMIT_NOBIND, // 禁止"发起/接收相依相偎"
PLAYER_LIMIT_MAX,
};
@@ -88,6 +88,24 @@ namespace BrewMonster
return 0;
}
public override void OnMsgPlayerExtState(ECMSG Msg)
{
base.OnMsgPlayerExtState(Msg);
if (m_aIconStates != null && m_aIconStates.Count > 0)
{
foreach (var state in m_aIconStates)
{
if (state.id == 287) // Thunder ball logic
{
// TODO: Implement UI logic for Ice Thunder Ball
// bool bHideIceThunderBall = CECUIHelper.GetGame().GetConfigs().GetGameSettings().bHideIceThunderBall;
// if (!bHideIceThunderBall) ...
break;
}
}
}
}
void OnMsgHstPetOpt(ECMSG Msg)
{
CECGameRun pGameRun = EC_Game.GetGameRun();
@@ -178,7 +196,7 @@ namespace BrewMonster
int tid = pCmd.pet_id;
int nid = m_pPetCorral.GetActivePetNPCID();
// 宠物有话说
// 宠物有话说
switch (pCmd.reason)
{
case (char)PET_RECALL_REASON.PET_RECALL_DEFAULT:
@@ -362,7 +380,7 @@ namespace BrewMonster
{
cmd_pet_ai_state pCmd = GPDataTypeHelper.FromBytes<cmd_pet_ai_state>((byte[])Msg.dwParam1);
// 宠物有话说
// 宠物有话说
CECPetData pPetData = m_pPetCorral.GetActivePet();
if (pPetData != null)
{
+50 -80
View File
@@ -1817,9 +1817,9 @@ namespace BrewMonster
}
/// <summary>
/// Cycles through learned skills by removing all shortcuts and adding 2 new skills to slots 0 and 1.
/// If m_startingSkillID is set (>0), uses that specific skill ID and the next one (ID+1).
/// Otherwise, cycles through all learned skills in pairs.
/// Cycles through learned skills by removing all shortcuts and adding 8 new skills to slots 0-7.
/// If m_startingSkillID is set (>0), uses that specific skill ID and the next 7 (ID+1 to ID+7).
/// Otherwise, cycles through all learned skills in groups of 8.
/// </summary>
public void CycleSkillShortcuts()
{
@@ -1833,74 +1833,51 @@ namespace BrewMonster
// Remove all shortcuts
pSCS.RemoveAllShortcuts();
const int skillsPerCycle = 8;
// If starting skill ID is configured, cycle through pairs starting from that ID
// If starting skill ID is configured, cycle through groups of 8 starting from that ID
if (m_startingSkillID > 0)
{
// Calculate the current skill IDs based on cycle index
// First press: startingID + 0, startingID + 1 (e.g., 5, 6)
// Second press: startingID + 2, startingID + 3 (e.g., 7, 8)
// Third press: startingID + 4, startingID + 5 (e.g., 9, 10)
int currentSkillID1 = m_startingSkillID + (m_currentSkillCycleIndex * 2);
int currentSkillID2 = currentSkillID1 + 1;
BMLogger.LogError($"CycleSkillShortcuts: Trying to add skills {currentSkillID1} and {currentSkillID2}");
// First press: startingID + 0 to startingID + 7 (e.g., 5-12)
// Second press: startingID + 8 to startingID + 15 (e.g., 13-20)
// Third press: startingID + 16 to startingID + 23 (e.g., 21-28)
int baseSkillID = m_startingSkillID + (m_currentSkillCycleIndex * skillsPerCycle);
BMLogger.LogError($"CycleSkillShortcuts: Trying to add skills {baseSkillID} to {baseSkillID + skillsPerCycle - 1}");
CECSkill pSkill1 = GetPositiveSkillByID(currentSkillID1);
if (pSkill1 != null)
// Add 8 skills to slots 0-7
for (int slot = 0; slot < skillsPerCycle; slot++)
{
pSCS.CreateSkillShortcut(0, pSkill1);
Debug.LogError($"CycleSkillShortcuts: Added skill ID {currentSkillID1} to slot 0");
}
else
{
Debug.LogError($"CycleSkillShortcuts: Skill with ID {currentSkillID1} not found in learned skills");
int find = 1;
while (pSkill1 == null && find < 100)
int currentSkillID = baseSkillID + slot;
CECSkill pSkill = GetPositiveSkillByID(currentSkillID);
if (pSkill != null)
{
m_startingSkillID++;
Debug.LogError($"CycleSkillShortcuts: m_startingSkillID {m_startingSkillID} ");
currentSkillID1 = m_startingSkillID + (m_currentSkillCycleIndex * 2);
pSkill1 = GetPositiveSkillByID(currentSkillID1);
find++;
}
if (pSkill1 != null)
{
pSCS.CreateSkillShortcut(0, pSkill1);
pSCS.CreateSkillShortcut(slot, pSkill);
Debug.LogError($"CycleSkillShortcuts: Added skill ID {currentSkillID} to slot {slot}");
}
else
{
Debug.LogError($"CycleSkillShortcuts: Failed to find skill after {find} attempts");
}
}
Debug.LogError($"CycleSkillShortcuts: Skill with ID {currentSkillID} not found in learned skills");
// Try to find a valid skill by incrementing starting ID
int find = 1;
while (pSkill == null && find < 100)
{
m_startingSkillID++;
Debug.LogError($"CycleSkillShortcuts: m_startingSkillID {m_startingSkillID}");
currentSkillID = m_startingSkillID + (m_currentSkillCycleIndex * skillsPerCycle) + slot;
pSkill = GetPositiveSkillByID(currentSkillID);
find++;
}
// Find and add second skill
CECSkill pSkill2 = GetPositiveSkillByID(currentSkillID2);
if (pSkill2 != null)
{
pSCS.CreateSkillShortcut(1, pSkill2);
Debug.LogError($"CycleSkillShortcuts: Added skill ID {currentSkillID2} to slot 1");
}
else
{
Debug.LogError($"CycleSkillShortcuts: Skill with ID {currentSkillID2} not found in learned skills");
int find = 1;
while (pSkill2 == null && find < 100)
{
m_startingSkillID++;
Debug.LogError($"CycleSkillShortcuts: m_startingSkillID {m_startingSkillID} ");
currentSkillID2 = m_startingSkillID + (m_currentSkillCycleIndex * 2) + 1;
pSkill2 = GetPositiveSkillByID(currentSkillID2);
find++;
}
if (pSkill2 != null)
{
pSCS.CreateSkillShortcut(1, pSkill2);
}
else
{
Debug.LogError($"CycleSkillShortcuts: Failed to find skill after {find} attempts");
if (pSkill != null)
{
pSCS.CreateSkillShortcut(slot, pSkill);
}
else
{
Debug.LogError($"CycleSkillShortcuts: Failed to find skill for slot {slot} after {find} attempts");
}
}
}
@@ -1926,36 +1903,29 @@ namespace BrewMonster
return;
}
// Calculate how many pairs we can make
int maxPairs = (skillCount + 1) / 2; // Round up division
// Calculate how many groups of 8 we can make
int maxGroups = (skillCount + skillsPerCycle - 1) / skillsPerCycle; // Round up division
// Wrap around if we've reached the end
if (m_currentSkillCycleIndex >= maxPairs)
if (m_currentSkillCycleIndex >= maxGroups)
{
m_currentSkillCycleIndex = 0;
}
// Calculate skill indices for this cycle
int skillIndex1 = m_currentSkillCycleIndex * 2;
int skillIndex2 = skillIndex1 + 1;
int baseSkillIndex = m_currentSkillCycleIndex * skillsPerCycle;
// Add first skill to slot 0
if (skillIndex1 < skillCount)
// Add up to 8 skills to slots 0-7
for (int slot = 0; slot < skillsPerCycle; slot++)
{
CECSkill pSkill1 = GetPositiveSkillByIndex(skillIndex1);
if (pSkill1 != null)
int skillIndex = baseSkillIndex + slot;
if (skillIndex < skillCount)
{
pSCS.CreateSkillShortcut(0, pSkill1);
}
}
// Add second skill to slot 1 (if available)
if (skillIndex2 < skillCount)
{
CECSkill pSkill2 = GetPositiveSkillByIndex(skillIndex2);
if (pSkill2 != null)
{
pSCS.CreateSkillShortcut(1, pSkill2);
CECSkill pSkill = GetPositiveSkillByIndex(skillIndex);
if (pSkill != null)
{
pSCS.CreateSkillShortcut(slot, pSkill);
}
}
}
File diff suppressed because one or more lines are too long