yuanshen/Yuanshen.Gameserver/Controllers/AvatarController.cs
wisedev fc2ac1050f First working version of the server
KCP gameserver with packet dispatch, scene streaming, combat, gacha and the
shop, plus a small HTTP server for the login flow and the wish history page.

Game data under assets is pulled out of the client instead of being typed by
hand. The excel tables come from the packed bin container and the spawn points
from the compiled scene scripts.
2026-09-04 02:10:50 +03:00

137 lines
5.1 KiB
C#

using Yuanshen.Gameserver.Controllers.Attributes;
using Yuanshen.Gameserver.Controllers.Result;
using Yuanshen.Gameserver.Database;
using Yuanshen.Gameserver.Game;
using Yuanshen.Gameserver.Network.Session;
using Yuanshen.Protocol;
using Microsoft.Extensions.Logging;
namespace Yuanshen.Gameserver.Controllers;
[NetController]
internal sealed class AvatarController : ControllerBase
{
[NetCommand(CmdType.SetUpAvatarTeamReq)]
public async ValueTask<IResult> OnSetUpAvatarTeamReq(Player player, SceneManager sceneManager, PlayerPersistence persistence)
{
SetUpAvatarTeamReq req = Packet!.DecodeBody<SetUpAvatarTeamReq>();
if (req.AvatarTeamGuidList.Count == 0)
return Response(CmdType.SetUpAvatarTeamRsp, new SetUpAvatarTeamRsp { Retcode = -1 });
await sceneManager.SetupTeamAsync(req.TeamId, req.AvatarTeamGuidList, req.CurAvatarGuid);
await persistence.SaveAsync();
SetUpAvatarTeamRsp rsp = new()
{
Retcode = 0,
TeamId = req.TeamId,
CurAvatarGuid = player.CurrentAvatarGuid
};
rsp.AvatarTeamGuidList.AddRange(req.AvatarTeamGuidList);
return Response(CmdType.SetUpAvatarTeamRsp, rsp);
}
[NetCommand(CmdType.ChangeAvatarReq)]
public async ValueTask<IResult> OnChangeAvatarReq(SceneManager sceneManager, PlayerPersistence persistence)
{
ChangeAvatarReq req = Packet!.DecodeBody<ChangeAvatarReq>();
await sceneManager.ChangeAvatarAsync(req.Guid);
await persistence.SaveAsync();
return Response(CmdType.ChangeAvatarRsp, new ChangeAvatarRsp
{
Retcode = 0,
CurrGuid = req.Guid,
SkillId = req.SkillId
});
}
private static bool OnScene(Player player, GameAvatar avatar)
=> player.TeamAvatarGuids.Contains(avatar.Guid);
[NetCommand(CmdType.WearEquipReq)]
public async ValueTask<IResult> OnWearEquipReq(NetSession session, Player player, SceneManager sceneManager, PlayerPersistence persistence, ILogger<AvatarController> logger)
{
WearEquipReq req = Packet!.DecodeBody<WearEquipReq>();
GameAvatar? avatar = player.FindAvatar(req.AvatarGuid);
GameWeapon? weapon = player.FindWeapon(req.EquipGuid);
GameReliquary? reliquary = player.FindReliquary(req.EquipGuid);
logger.LogDebug("WearEquip avatarGuid={a} equipGuid={e} -> avatar={av} weapon={w} reliquary={r}",
req.AvatarGuid, req.EquipGuid,
avatar is null ? "NOT FOUND" : avatar.AvatarId.ToString(),
weapon is null ? "-" : weapon.ItemId.ToString(),
reliquary is null ? "-" : reliquary.ItemId.ToString());
if (avatar is not null && weapon is not null)
{
avatar.WeaponGuid = weapon.Guid;
await session.NotifyAsync(CmdType.AvatarEquipChangeNotify, new AvatarEquipChangeNotify
{
AvatarGuid = avatar.Guid,
EquipType = 6,
ItemId = weapon.ItemId,
EquipGuid = weapon.Guid,
Weapon = weapon.AsSceneWeapon(OnScene(player, avatar) ? avatar.WeaponEntityId : 0)
});
}
else if (avatar is not null && reliquary is not null)
{
avatar.ReliquaryGuids[reliquary.EquipType] = reliquary.Guid;
await session.NotifyAsync(CmdType.AvatarEquipChangeNotify, new AvatarEquipChangeNotify
{
AvatarGuid = avatar.Guid,
EquipType = reliquary.EquipType,
ItemId = reliquary.ItemId,
EquipGuid = reliquary.Guid,
Reliquary = reliquary.AsSceneReliquary()
});
}
if (avatar is not null && player.TeamAvatarGuids.Contains(avatar.Guid))
await session.NotifyAsync(CmdType.SceneTeamUpdateNotify, sceneManager.BuildSceneTeamUpdate());
if (avatar is not null)
await persistence.SaveAsync();
return Response(CmdType.WearEquipRsp, new WearEquipRsp
{
Retcode = 0,
AvatarGuid = req.AvatarGuid,
EquipGuid = req.EquipGuid
});
}
[NetCommand(CmdType.AvatarWearFlycloakReq)]
public async ValueTask<IResult> OnAvatarWearFlycloakReq(Player player, NetSession session, PlayerPersistence persistence)
{
AvatarWearFlycloakReq req = Packet!.DecodeBody<AvatarWearFlycloakReq>();
GameAvatar? avatar = player.Avatars.FirstOrDefault(candidate => candidate.Guid == req.AvatarGuid);
if (avatar is null)
return Response(CmdType.AvatarWearFlycloakRsp, new AvatarWearFlycloakRsp { Retcode = Retcode.RET_AVATAR_ID_ERROR });
avatar.FlycloakId = req.FlycloakId;
await session.NotifyAsync(CmdType.AvatarFlycloakChangeNotify, new AvatarFlycloakChangeNotify
{
AvatarGuid = req.AvatarGuid,
FlycloakId = req.FlycloakId
});
await persistence.SaveAsync();
return Response(CmdType.AvatarWearFlycloakRsp, new AvatarWearFlycloakRsp
{
Retcode = Retcode.RET_SUCC,
AvatarGuid = req.AvatarGuid,
FlycloakId = req.FlycloakId
});
}
}