mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-05-06 22:35:11 +02:00
Phase C of #188 — the last phase of server-side multi-language support. Adds Session.I18n(), a cached per-session i18n table resolver built via getLangStringsFor(s.Lang()). The pointer is stable until SetLang invalidates the cache, so hot-path handlers pay zero allocations on repeated calls. All 51 s.server.i18n.* call sites across commands, guild, guild scout, cafe, and cast-binary handlers now route through s.I18n().*, so chat replies, guild invite mail templates, cafe reset notices, and quest-timer broadcasts are served in the player's preferred language instead of the server-wide default. Scenario JSON gets the same plain-or-map LocalizedString treatment that quests received in phase B: subheader Strings and inline entry Text accept either a plain string (backwards compatible) or a language-keyed object. CompileScenarioJSON takes the compiling session's language, loadScenarioBinary passes s.Lang(), and ParseScenarioBinary emits plain-string LocalizedStrings so existing .bin files round-trip byte-for-byte through the JSON path. World-wide broadcasts (Raviente siege announcements via BroadcastRaviente) intentionally stay on the server default — they have no single-session context to resolve against.
191 lines
4.8 KiB
Go
191 lines
4.8 KiB
Go
package channelserver
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"erupe-ce/common/byteframe"
|
|
"erupe-ce/common/stringsupport"
|
|
"erupe-ce/network/mhfpacket"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func handleMsgMhfPostGuildScout(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfPostGuildScout)
|
|
|
|
err := s.server.guildService.PostScout(s.charID, pkt.CharID, ScoutInviteStrings{
|
|
Title: s.I18n().guild.invite.title,
|
|
Body: s.I18n().guild.invite.body,
|
|
})
|
|
|
|
if errors.Is(err, ErrAlreadyInvited) {
|
|
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x04})
|
|
return
|
|
}
|
|
if err != nil {
|
|
s.logger.Error("Failed to post guild scout", zap.Error(err))
|
|
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
|
|
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
|
}
|
|
|
|
func handleMsgMhfCancelGuildScout(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfCancelGuildScout)
|
|
|
|
guildCharData, err := s.server.guildRepo.GetCharacterMembership(s.charID)
|
|
|
|
if err != nil {
|
|
s.logger.Error("Failed to get character guild data for cancel scout", zap.Error(err))
|
|
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
|
|
if guildCharData == nil || !guildCharData.CanRecruit() {
|
|
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
|
|
guild, err := s.server.guildRepo.GetByID(guildCharData.GuildID)
|
|
|
|
if err != nil || guild == nil {
|
|
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
|
|
err = s.server.guildRepo.CancelInvite(pkt.InvitationID)
|
|
|
|
if err != nil {
|
|
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
|
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
}
|
|
|
|
func handleMsgMhfAnswerGuildScout(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfAnswerGuildScout)
|
|
|
|
i := s.I18n().guild.invite
|
|
result, err := s.server.guildService.AnswerScout(s.charID, pkt.LeaderID, pkt.Answer, AnswerScoutStrings{
|
|
SuccessTitle: i.success.title,
|
|
SuccessBody: i.success.body,
|
|
AcceptedTitle: i.accepted.title,
|
|
AcceptedBody: i.accepted.body,
|
|
RejectedTitle: i.rejected.title,
|
|
RejectedBody: i.rejected.body,
|
|
DeclinedTitle: i.declined.title,
|
|
DeclinedBody: i.declined.body,
|
|
})
|
|
|
|
if err != nil && !errors.Is(err, ErrApplicationMissing) {
|
|
s.logger.Error("Failed to answer guild scout", zap.Error(err))
|
|
doAckBufFail(s, pkt.AckHandle, nil)
|
|
return
|
|
}
|
|
|
|
bf := byteframe.NewByteFrame()
|
|
if result != nil && result.Success {
|
|
bf.WriteUint32(0)
|
|
bf.WriteUint32(result.GuildID)
|
|
} else {
|
|
if errors.Is(err, ErrApplicationMissing) {
|
|
s.logger.Warn("Guild invite missing, deleted?",
|
|
zap.Uint32("charID", s.charID))
|
|
}
|
|
bf.WriteUint32(7)
|
|
if result != nil {
|
|
bf.WriteUint32(result.GuildID)
|
|
} else {
|
|
bf.WriteUint32(0)
|
|
}
|
|
}
|
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
|
}
|
|
|
|
func handleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfGetGuildScoutList)
|
|
|
|
guildInfo, err := s.server.guildRepo.GetByCharID(s.charID)
|
|
if err != nil {
|
|
s.logger.Warn("Failed to get guild for scout list", zap.Error(err))
|
|
}
|
|
|
|
if guildInfo == nil {
|
|
if s.prevGuildID == 0 {
|
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
guildInfo, err = s.server.guildRepo.GetByID(s.prevGuildID)
|
|
if guildInfo == nil || err != nil {
|
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
}
|
|
|
|
invites, err := s.server.guildRepo.ListInvites(guildInfo.ID)
|
|
if err != nil {
|
|
s.logger.Error("failed to retrieve scout invites", zap.Error(err))
|
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetBE()
|
|
bf.WriteUint32(uint32(len(invites)))
|
|
|
|
for _, inv := range invites {
|
|
bf.WriteUint32(inv.ID)
|
|
bf.WriteUint32(inv.ActorID)
|
|
bf.WriteUint32(inv.CharID)
|
|
bf.WriteUint32(uint32(inv.InvitedAt.Unix()))
|
|
bf.WriteUint16(inv.HR)
|
|
bf.WriteUint16(inv.GR)
|
|
bf.WriteBytes(stringsupport.PaddedString(inv.Name, 32, true))
|
|
}
|
|
|
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
|
}
|
|
|
|
func handleMsgMhfGetRejectGuildScout(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfGetRejectGuildScout)
|
|
|
|
currentStatus, err := s.server.charRepo.ReadBool(s.charID, "restrict_guild_scout")
|
|
|
|
if err != nil {
|
|
s.logger.Error(
|
|
"failed to retrieve character guild scout status",
|
|
zap.Error(err),
|
|
zap.Uint32("charID", s.charID),
|
|
)
|
|
doAckSimpleFail(s, pkt.AckHandle, nil)
|
|
return
|
|
}
|
|
|
|
response := uint8(0x00)
|
|
|
|
if currentStatus {
|
|
response = 0x01
|
|
}
|
|
|
|
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, response})
|
|
}
|
|
|
|
func handleMsgMhfSetRejectGuildScout(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfSetRejectGuildScout)
|
|
|
|
err := s.server.charRepo.SaveBool(s.charID, "restrict_guild_scout", pkt.Reject)
|
|
|
|
if err != nil {
|
|
s.logger.Error(
|
|
"failed to update character guild scout status",
|
|
zap.Error(err),
|
|
zap.Uint32("charID", s.charID),
|
|
)
|
|
doAckSimpleFail(s, pkt.AckHandle, nil)
|
|
return
|
|
}
|
|
|
|
doAckSimpleSucceed(s, pkt.AckHandle, nil)
|
|
}
|