From 2bd5f98f32f373da64c3a1f01d407ba388ff65eb Mon Sep 17 00:00:00 2001 From: Houmgaor Date: Wed, 18 Feb 2026 21:39:13 +0100 Subject: [PATCH] docs: add doc.go files and godoc comments to all packages Add package-level documentation (doc.go) to all 22 first-party packages and godoc comments to ~150 previously undocumented exported symbols across common/, network/, and server/. --- common/bfutil/doc.go | 3 +++ common/byteframe/byteframe.go | 1 + common/byteframe/doc.go | 4 +++ common/decryption/doc.go | 4 +++ common/decryption/jpk.go | 9 +++++++ common/gametime/doc.go | 4 +++ common/gametime/gametime.go | 6 +++++ common/mhfcid/doc.go | 3 +++ common/mhfcourse/doc.go | 5 ++++ common/mhfcourse/mhfcourse.go | 4 +++ common/mhfitem/doc.go | 4 +++ common/mhfitem/mhfitem.go | 20 ++++++++++++++ common/mhfmon/doc.go | 4 +++ common/mhfmon/mhfmon.go | 2 ++ common/pascalstring/doc.go | 4 +++ common/pascalstring/pascalstring.go | 6 +++++ common/stringstack/doc.go | 3 +++ common/stringsupport/doc.go | 5 ++++ common/stringsupport/string_convert.go | 16 ++++++++++++ common/token/doc.go | 3 +++ common/token/token.go | 6 +++++ network/binpacket/doc.go | 4 +++ network/binpacket/msg_bin_mail_notify.go | 4 +++ network/clientctx/doc.go | 3 +++ network/crypto/doc.go | 5 ++++ network/doc.go | 5 ++++ network/mhfpacket/doc.go | 4 +++ network/mhfpacket/msg_mhf_enumerate_guild.go | 1 + network/mhfpacket/msg_mhf_operate_guild.go | 1 + .../mhfpacket/msg_mhf_operate_guild_member.go | 1 + network/mhfpacket/msg_mhf_operate_joint.go | 1 + network/mhfpacket/msg_mhf_oprt_mail.go | 1 + .../msg_mhf_set_ca_achievement_hist.go | 1 + network/mhfpacket/msg_mhf_update_guacot.go | 1 + .../mhfpacket/msg_mhf_update_guild_icon.go | 1 + network/packetid.go | 2 ++ server/api/api_server.go | 1 + server/api/doc.go | 5 ++++ server/api/endpoints.go | 26 +++++++++++++++++++ .../compression/deltacomp/doc.go | 3 +++ .../channelserver/compression/nullcomp/doc.go | 4 +++ server/channelserver/doc.go | 11 ++++++++ server/channelserver/guild_model.go | 11 ++++++++ server/channelserver/handlers_achievement.go | 2 ++ server/channelserver/handlers_cafe.go | 1 + server/channelserver/handlers_campaign.go | 3 +++ server/channelserver/handlers_caravan.go | 5 ++++ server/channelserver/handlers_character.go | 1 + server/channelserver/handlers_data_paper.go | 5 ++++ server/channelserver/handlers_distitem.go | 2 ++ server/channelserver/handlers_event.go | 1 + server/channelserver/handlers_gacha.go | 3 +++ .../channelserver/handlers_guild_adventure.go | 1 + .../channelserver/handlers_guild_alliance.go | 2 ++ server/channelserver/handlers_guild_board.go | 1 + .../channelserver/handlers_guild_cooking.go | 1 + server/channelserver/handlers_guild_member.go | 3 +++ .../channelserver/handlers_guild_mission.go | 1 + .../channelserver/handlers_guild_tresure.go | 2 ++ server/channelserver/handlers_house.go | 2 ++ server/channelserver/handlers_mail.go | 4 +++ server/channelserver/handlers_mercenary.go | 2 ++ server/channelserver/handlers_misc.go | 1 + server/channelserver/handlers_quest.go | 1 + server/channelserver/handlers_register.go | 1 + server/channelserver/handlers_rengoku.go | 1 + server/channelserver/handlers_scenario.go | 1 + server/channelserver/handlers_seibattle.go | 9 +++++++ server/channelserver/handlers_shop.go | 2 ++ server/channelserver/handlers_tournament.go | 4 +++ server/channelserver/handlers_tower.go | 14 ++++++++++ server/channelserver/model_character.go | 2 ++ server/channelserver/raviente.go | 1 + server/channelserver/sys_channel_server.go | 8 ++++++ server/channelserver/sys_session.go | 1 + server/channelserver/sys_time.go | 5 ++++ server/discordbot/discord_bot.go | 12 +++++++++ server/discordbot/doc.go | 4 +++ server/entranceserver/doc.go | 12 +++++++++ server/signserver/doc.go | 5 ++++ server/signserver/respid.go | 4 +++ 81 files changed, 342 insertions(+) create mode 100644 common/bfutil/doc.go create mode 100644 common/byteframe/doc.go create mode 100644 common/decryption/doc.go create mode 100644 common/gametime/doc.go create mode 100644 common/mhfcid/doc.go create mode 100644 common/mhfcourse/doc.go create mode 100644 common/mhfitem/doc.go create mode 100644 common/mhfmon/doc.go create mode 100644 common/pascalstring/doc.go create mode 100644 common/stringstack/doc.go create mode 100644 common/stringsupport/doc.go create mode 100644 common/token/doc.go create mode 100644 network/binpacket/doc.go create mode 100644 network/clientctx/doc.go create mode 100644 network/crypto/doc.go create mode 100644 network/doc.go create mode 100644 network/mhfpacket/doc.go create mode 100644 server/api/doc.go create mode 100644 server/channelserver/compression/deltacomp/doc.go create mode 100644 server/channelserver/compression/nullcomp/doc.go create mode 100644 server/channelserver/doc.go create mode 100644 server/discordbot/doc.go create mode 100644 server/entranceserver/doc.go create mode 100644 server/signserver/doc.go diff --git a/common/bfutil/doc.go b/common/bfutil/doc.go new file mode 100644 index 000000000..b4b832338 --- /dev/null +++ b/common/bfutil/doc.go @@ -0,0 +1,3 @@ +// Package bfutil provides byte-slice utility functions for working with +// null-terminated binary data commonly found in MHF network packets. +package bfutil diff --git a/common/byteframe/byteframe.go b/common/byteframe/byteframe.go index 6980b2e4d..94478441a 100644 --- a/common/byteframe/byteframe.go +++ b/common/byteframe/byteframe.go @@ -135,6 +135,7 @@ func (b *ByteFrame) DataFromCurrent() []byte { return b.buf[b.index:b.usedSize] } +// Index returns the current read/write position in the buffer. func (b *ByteFrame) Index() uint { return b.index } diff --git a/common/byteframe/doc.go b/common/byteframe/doc.go new file mode 100644 index 000000000..325629a7e --- /dev/null +++ b/common/byteframe/doc.go @@ -0,0 +1,4 @@ +// Package byteframe provides a seekable, growable byte buffer for reading and +// writing binary data in big-endian or little-endian byte order. It is the +// primary serialization primitive used throughout the Erupe network layer. +package byteframe diff --git a/common/decryption/doc.go b/common/decryption/doc.go new file mode 100644 index 000000000..a05ca0aa9 --- /dev/null +++ b/common/decryption/doc.go @@ -0,0 +1,4 @@ +// Package decryption implements the JPK decompression algorithm used by +// Monster Hunter Frontier to compress game data files. The format is +// identified by the magic bytes 0x1A524B4A ("JKR"). +package decryption diff --git a/common/decryption/jpk.go b/common/decryption/jpk.go index 3925aaec7..24940f178 100644 --- a/common/decryption/jpk.go +++ b/common/decryption/jpk.go @@ -13,6 +13,8 @@ import ( var mShiftIndex = 0 var mFlag = byte(0) +// UnpackSimple decompresses a JPK type-3 compressed byte slice. If the data +// does not start with the JKR magic header it is returned unchanged. func UnpackSimple(data []byte) []byte { mShiftIndex = 0 mFlag = byte(0) @@ -40,6 +42,8 @@ func UnpackSimple(data []byte) []byte { return data } +// ProcessDecode runs the JPK LZ-style decompression loop, reading compressed +// tokens from data and writing decompressed bytes into outBuffer. func ProcessDecode(data *byteframe.ByteFrame, outBuffer []byte) { outIndex := 0 @@ -85,6 +89,8 @@ func ProcessDecode(data *byteframe.ByteFrame, outBuffer []byte) { } } +// JPKBitShift reads one bit from the compressed stream's flag byte, refilling +// the flag from the next byte in data when all 8 bits have been consumed. func JPKBitShift(data *byteframe.ByteFrame) byte { mShiftIndex-- @@ -96,6 +102,8 @@ func JPKBitShift(data *byteframe.ByteFrame) byte { return (byte)((mFlag >> mShiftIndex) & 1) } +// JPKCopy copies length bytes from a previous position in outBuffer (determined +// by offset back from the current index) to implement LZ back-references. func JPKCopy(outBuffer []byte, offset int, length int, index *int) { for i := 0; i < length; i++ { outBuffer[*index] = outBuffer[*index-offset-1] @@ -103,6 +111,7 @@ func JPKCopy(outBuffer []byte, offset int, length int, index *int) { } } +// ReadByte reads a single byte from the ByteFrame. func ReadByte(bf *byteframe.ByteFrame) byte { value := bf.ReadUint8() return value diff --git a/common/gametime/doc.go b/common/gametime/doc.go new file mode 100644 index 000000000..9662b26ea --- /dev/null +++ b/common/gametime/doc.go @@ -0,0 +1,4 @@ +// Package gametime provides time helpers anchored to the JST (UTC+9) timezone +// used by Monster Hunter Frontier's game clock, including weekly reset +// boundaries and the in-game absolute time cycle. +package gametime diff --git a/common/gametime/gametime.go b/common/gametime/gametime.go index c1a2b43cd..1612a0831 100644 --- a/common/gametime/gametime.go +++ b/common/gametime/gametime.go @@ -4,16 +4,19 @@ import ( "time" ) +// Adjusted returns the current time in JST (UTC+9), the timezone used by MHF. func Adjusted() time.Time { baseTime := time.Now().In(time.FixedZone("UTC+9", 9*60*60)) return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), baseTime.Hour(), baseTime.Minute(), baseTime.Second(), baseTime.Nanosecond(), baseTime.Location()) } +// Midnight returns today's midnight (00:00) in JST. func Midnight() time.Time { baseTime := time.Now().In(time.FixedZone("UTC+9", 9*60*60)) return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), 0, 0, 0, 0, baseTime.Location()) } +// WeekStart returns the most recent Monday at midnight in JST. func WeekStart() time.Time { midnight := Midnight() offset := int(midnight.Weekday()) - int(time.Monday) @@ -23,10 +26,13 @@ func WeekStart() time.Time { return midnight.Add(-time.Duration(offset) * 24 * time.Hour) } +// WeekNext returns the next Monday at midnight in JST. func WeekNext() time.Time { return WeekStart().Add(time.Hour * 24 * 7) } +// GameAbsolute returns the current position within the 5760-second (96-minute) +// in-game day/night cycle, offset by 2160 seconds. func GameAbsolute() uint32 { return uint32((Adjusted().Unix() - 2160) % 5760) } diff --git a/common/mhfcid/doc.go b/common/mhfcid/doc.go new file mode 100644 index 000000000..68138da0e --- /dev/null +++ b/common/mhfcid/doc.go @@ -0,0 +1,3 @@ +// Package mhfcid converts MHF Character ID strings (a base-32 encoding that +// omits the ambiguous characters 0, I, O, and S) to their numeric equivalents. +package mhfcid diff --git a/common/mhfcourse/doc.go b/common/mhfcourse/doc.go new file mode 100644 index 000000000..824d75fe7 --- /dev/null +++ b/common/mhfcourse/doc.go @@ -0,0 +1,5 @@ +// Package mhfcourse models the subscription course system used by Monster +// Hunter Frontier. Courses (Trial, HunterLife, Extra, Premium, etc.) are +// represented as bit flags in a uint32 rights field and control which game +// features a player can access. +package mhfcourse diff --git a/common/mhfcourse/mhfcourse.go b/common/mhfcourse/mhfcourse.go index 71ccc0ab7..0036c9a93 100644 --- a/common/mhfcourse/mhfcourse.go +++ b/common/mhfcourse/mhfcourse.go @@ -7,6 +7,7 @@ import ( "time" ) +// Course represents an active subscription course with its ID and expiry time. type Course struct { ID uint16 Expiry time.Time @@ -39,10 +40,12 @@ var aliases = map[uint16][]string{ // 30 = Real NetCafe course } +// Aliases returns the human-readable names for this course (e.g. "HunterLife", "HL"). func (c Course) Aliases() []string { return aliases[c.ID] } +// Courses returns all 32 possible course slots with zero-value expiry times. func Courses() []Course { courses := make([]Course, 32) for i := range courses { @@ -51,6 +54,7 @@ func Courses() []Course { return courses } +// Value returns the bitmask value for this course (2^ID). func (c Course) Value() uint32 { return uint32(math.Pow(2, float64(c.ID))) } diff --git a/common/mhfitem/doc.go b/common/mhfitem/doc.go new file mode 100644 index 000000000..4f94cef97 --- /dev/null +++ b/common/mhfitem/doc.go @@ -0,0 +1,4 @@ +// Package mhfitem defines item, equipment, and sigil data structures as they +// appear in the MHF binary protocol, and provides serialization helpers for +// warehouse (box/storage) operations. +package mhfitem diff --git a/common/mhfitem/mhfitem.go b/common/mhfitem/mhfitem.go index 58fbf45e7..9486a6714 100644 --- a/common/mhfitem/mhfitem.go +++ b/common/mhfitem/mhfitem.go @@ -6,15 +6,18 @@ import ( _config "erupe-ce/config" ) +// MHFItem represents a single item identified by its in-game item ID. type MHFItem struct { ItemID uint16 } +// MHFSigilEffect represents a single effect slot on a sigil with an ID and level. type MHFSigilEffect struct { ID uint16 Level uint16 } +// MHFSigil represents a weapon sigil containing up to three effects. type MHFSigil struct { Effects []MHFSigilEffect Unk0 uint8 @@ -23,6 +26,8 @@ type MHFSigil struct { Unk3 uint8 } +// MHFEquipment represents an equipment piece (weapon or armor) with its +// decorations and sigils as stored in the player's warehouse. type MHFEquipment struct { WarehouseID uint32 ItemType uint8 @@ -34,6 +39,7 @@ type MHFEquipment struct { Unk1 uint16 } +// MHFItemStack represents a stacked item slot in the warehouse with a quantity. type MHFItemStack struct { WarehouseID uint32 Item MHFItem @@ -41,6 +47,8 @@ type MHFItemStack struct { Unk0 uint32 } +// ReadWarehouseItem deserializes an MHFItemStack from a ByteFrame, assigning a +// random warehouse ID if the encoded ID is zero. func ReadWarehouseItem(bf *byteframe.ByteFrame) MHFItemStack { var item MHFItemStack item.WarehouseID = bf.ReadUint32() @@ -53,6 +61,9 @@ func ReadWarehouseItem(bf *byteframe.ByteFrame) MHFItemStack { return item } +// DiffItemStacks merges an updated item stack list into an existing one, +// matching by warehouse ID. New items receive a random ID; items with zero +// quantity in the old list are removed. func DiffItemStacks(o []MHFItemStack, u []MHFItemStack) []MHFItemStack { // o = old, u = update, f = final var f []MHFItemStack @@ -77,6 +88,7 @@ func DiffItemStacks(o []MHFItemStack, u []MHFItemStack) []MHFItemStack { return f } +// ToBytes serializes the item stack to its binary protocol representation. func (is MHFItemStack) ToBytes() []byte { bf := byteframe.NewByteFrame() bf.WriteUint32(is.WarehouseID) @@ -86,6 +98,8 @@ func (is MHFItemStack) ToBytes() []byte { return bf.Data() } +// SerializeWarehouseItems serializes a slice of item stacks with a uint16 +// count header for transmission in warehouse response packets. func SerializeWarehouseItems(i []MHFItemStack) []byte { bf := byteframe.NewByteFrame() bf.WriteUint16(uint16(len(i))) @@ -96,6 +110,9 @@ func SerializeWarehouseItems(i []MHFItemStack) []byte { return bf.Data() } +// ReadWarehouseEquipment deserializes an MHFEquipment from a ByteFrame. The +// binary layout varies by game version: sigils are present from G1 onward and +// an additional field is present from Z1 onward. func ReadWarehouseEquipment(bf *byteframe.ByteFrame) MHFEquipment { var equipment MHFEquipment equipment.Decorations = make([]MHFItem, 3) @@ -134,6 +151,7 @@ func ReadWarehouseEquipment(bf *byteframe.ByteFrame) MHFEquipment { return equipment } +// ToBytes serializes the equipment to its binary protocol representation. func (e MHFEquipment) ToBytes() []byte { bf := byteframe.NewByteFrame() bf.WriteUint32(e.WarehouseID) @@ -164,6 +182,8 @@ func (e MHFEquipment) ToBytes() []byte { return bf.Data() } +// SerializeWarehouseEquipment serializes a slice of equipment with a uint16 +// count header for transmission in warehouse response packets. func SerializeWarehouseEquipment(i []MHFEquipment) []byte { bf := byteframe.NewByteFrame() bf.WriteUint16(uint16(len(i))) diff --git a/common/mhfmon/doc.go b/common/mhfmon/doc.go new file mode 100644 index 000000000..be9994764 --- /dev/null +++ b/common/mhfmon/doc.go @@ -0,0 +1,4 @@ +// Package mhfmon enumerates every monster in Monster Hunter Frontier by its +// internal enemy ID (em001–em176) and provides metadata such as display name +// and large/small classification. +package mhfmon diff --git a/common/mhfmon/mhfmon.go b/common/mhfmon/mhfmon.go index e192844fe..6fcc6d508 100644 --- a/common/mhfmon/mhfmon.go +++ b/common/mhfmon/mhfmon.go @@ -180,11 +180,13 @@ const ( KingShakalaka ) +// Monster holds display metadata for a single monster species. type Monster struct { Name string Large bool } +// Monsters is an ordered table of all MHF monsters, indexed by enemy ID. var Monsters = []Monster{ {"Mon0", false}, {"Rathian", true}, diff --git a/common/pascalstring/doc.go b/common/pascalstring/doc.go new file mode 100644 index 000000000..82b3d7a97 --- /dev/null +++ b/common/pascalstring/doc.go @@ -0,0 +1,4 @@ +// Package pascalstring writes length-prefixed, null-terminated strings into a +// ByteFrame. The prefix width is selectable (uint8, uint16, or uint32) and +// strings are optionally encoded to Shift-JIS before writing. +package pascalstring diff --git a/common/pascalstring/pascalstring.go b/common/pascalstring/pascalstring.go index 8ad332018..ce4d60214 100644 --- a/common/pascalstring/pascalstring.go +++ b/common/pascalstring/pascalstring.go @@ -6,6 +6,8 @@ import ( "golang.org/x/text/transform" ) +// Uint8 writes x as a null-terminated string with a uint8 length prefix. If t +// is true the string is first encoded to Shift-JIS. func Uint8(bf *byteframe.ByteFrame, x string, t bool) { if t { e := japanese.ShiftJIS.NewEncoder() @@ -20,6 +22,8 @@ func Uint8(bf *byteframe.ByteFrame, x string, t bool) { bf.WriteNullTerminatedBytes([]byte(x)) } +// Uint16 writes x as a null-terminated string with a uint16 length prefix. If +// t is true the string is first encoded to Shift-JIS. func Uint16(bf *byteframe.ByteFrame, x string, t bool) { if t { e := japanese.ShiftJIS.NewEncoder() @@ -34,6 +38,8 @@ func Uint16(bf *byteframe.ByteFrame, x string, t bool) { bf.WriteNullTerminatedBytes([]byte(x)) } +// Uint32 writes x as a null-terminated string with a uint32 length prefix. If +// t is true the string is first encoded to Shift-JIS. func Uint32(bf *byteframe.ByteFrame, x string, t bool) { if t { e := japanese.ShiftJIS.NewEncoder() diff --git a/common/stringstack/doc.go b/common/stringstack/doc.go new file mode 100644 index 000000000..d61b53608 --- /dev/null +++ b/common/stringstack/doc.go @@ -0,0 +1,3 @@ +// Package stringstack provides a minimal LIFO stack for strings, used +// internally to track hierarchical state such as nested stage paths. +package stringstack diff --git a/common/stringsupport/doc.go b/common/stringsupport/doc.go new file mode 100644 index 000000000..f58c91c1d --- /dev/null +++ b/common/stringsupport/doc.go @@ -0,0 +1,5 @@ +// Package stringsupport provides string conversion utilities for the MHF +// protocol, including UTF-8 ↔ Shift-JIS transcoding, padded fixed-width +// string encoding, NG-word conversion, and comma-separated integer list +// manipulation used for database storage. +package stringsupport diff --git a/common/stringsupport/string_convert.go b/common/stringsupport/string_convert.go index 2d09138f8..79e2a8459 100644 --- a/common/stringsupport/string_convert.go +++ b/common/stringsupport/string_convert.go @@ -11,6 +11,8 @@ import ( "golang.org/x/text/transform" ) +// UTF8ToSJIS encodes a UTF-8 string to Shift-JIS bytes, silently dropping any +// runes that cannot be represented in Shift-JIS. func UTF8ToSJIS(x string) []byte { e := japanese.ShiftJIS.NewEncoder() xt, _, err := transform.String(e, x) @@ -28,6 +30,7 @@ func UTF8ToSJIS(x string) []byte { return []byte(xt) } +// SJISToUTF8 decodes Shift-JIS bytes to a UTF-8 string. func SJISToUTF8(b []byte) string { d := japanese.ShiftJIS.NewDecoder() result, err := io.ReadAll(transform.NewReader(bytes.NewReader(b), d)) @@ -37,6 +40,8 @@ func SJISToUTF8(b []byte) string { return string(result) } +// ToNGWord converts a UTF-8 string into a slice of uint16 values in the +// Shift-JIS byte-swapped format used by the MHF NG-word (chat filter) system. func ToNGWord(x string) []uint16 { var w []uint16 for _, r := range x { @@ -55,6 +60,8 @@ func ToNGWord(x string) []uint16 { return w } +// PaddedString returns a fixed-width null-terminated byte slice of the given +// size. If t is true the string is first encoded to Shift-JIS. func PaddedString(x string, size uint, t bool) []byte { if t { e := japanese.ShiftJIS.NewEncoder() @@ -70,6 +77,7 @@ func PaddedString(x string, size uint, t bool) []byte { return out } +// CSVAdd appends v to the comma-separated integer list if not already present. func CSVAdd(csv string, v int) string { if len(csv) == 0 { return strconv.Itoa(v) @@ -81,6 +89,7 @@ func CSVAdd(csv string, v int) string { } } +// CSVRemove removes v from the comma-separated integer list. func CSVRemove(csv string, v int) string { s := strings.Split(csv, ",") for i, e := range s { @@ -92,6 +101,7 @@ func CSVRemove(csv string, v int) string { return strings.Join(s, ",") } +// CSVContains reports whether v is present in the comma-separated integer list. func CSVContains(csv string, v int) bool { s := strings.Split(csv, ",") for i := 0; i < len(s); i++ { @@ -103,6 +113,7 @@ func CSVContains(csv string, v int) bool { return false } +// CSVLength returns the number of elements in the comma-separated list. func CSVLength(csv string) int { if csv == "" { return 0 @@ -111,6 +122,7 @@ func CSVLength(csv string) int { return len(s) } +// CSVElems parses the comma-separated integer list into an int slice. func CSVElems(csv string) []int { var r []int if csv == "" { @@ -124,6 +136,8 @@ func CSVElems(csv string) []int { return r } +// CSVGetIndex returns the integer at position i in the comma-separated list, +// or 0 if i is out of range. func CSVGetIndex(csv string, i int) int { s := CSVElems(csv) if i < len(s) { @@ -132,6 +146,8 @@ func CSVGetIndex(csv string, i int) int { return 0 } +// CSVSetIndex replaces the integer at position i in the comma-separated list +// with v. If i is out of range the list is returned unchanged. func CSVSetIndex(csv string, i int, v int) string { s := CSVElems(csv) if i < len(s) { diff --git a/common/token/doc.go b/common/token/doc.go new file mode 100644 index 000000000..19c252eff --- /dev/null +++ b/common/token/doc.go @@ -0,0 +1,3 @@ +// Package token provides concurrency-safe random number generation and +// alphanumeric token generation for session tokens and warehouse IDs. +package token diff --git a/common/token/token.go b/common/token/token.go index b4ccfe065..7e24eb50c 100644 --- a/common/token/token.go +++ b/common/token/token.go @@ -12,12 +12,15 @@ type SafeRand struct { rng *rand.Rand } +// NewSafeRand creates a SafeRand seeded with the current time. func NewSafeRand() *SafeRand { return &SafeRand{ rng: rand.New(rand.NewSource(time.Now().UnixNano())), } } +// Intn returns a non-negative pseudo-random int in [0,n). It is safe for +// concurrent use. func (sr *SafeRand) Intn(n int) int { sr.mu.Lock() v := sr.rng.Intn(n) @@ -25,6 +28,7 @@ func (sr *SafeRand) Intn(n int) int { return v } +// Uint32 returns a pseudo-random uint32. It is safe for concurrent use. func (sr *SafeRand) Uint32() uint32 { sr.mu.Lock() v := sr.rng.Uint32() @@ -32,6 +36,8 @@ func (sr *SafeRand) Uint32() uint32 { return v } +// RNG is the global concurrency-safe random number generator used throughout +// the server for generating warehouse IDs, session tokens, and other values. var RNG = NewSafeRand() // Generate returns an alphanumeric token of specified length diff --git a/network/binpacket/doc.go b/network/binpacket/doc.go new file mode 100644 index 000000000..a7457a318 --- /dev/null +++ b/network/binpacket/doc.go @@ -0,0 +1,4 @@ +// Package binpacket defines higher-level binary message types that are carried +// inside MSG_SYS_CAST_BINARY / MSG_SYS_CASTED_BINARY packets. These include +// chat messages, mail notifications, and targeted player broadcasts. +package binpacket diff --git a/network/binpacket/msg_bin_mail_notify.go b/network/binpacket/msg_bin_mail_notify.go index 125dc57ef..a0e6a9dd5 100644 --- a/network/binpacket/msg_bin_mail_notify.go +++ b/network/binpacket/msg_bin_mail_notify.go @@ -6,20 +6,24 @@ import ( "erupe-ce/network" ) +// MsgBinMailNotify is a binpacket broadcast to notify a player of new mail. type MsgBinMailNotify struct { SenderName string } +// Parse parses the packet from binary. func (m MsgBinMailNotify) Parse(bf *byteframe.ByteFrame) error { panic("implement me") } +// Build builds a binary packet from the current data. func (m MsgBinMailNotify) Build(bf *byteframe.ByteFrame) error { bf.WriteUint8(0x01) // Unk bf.WriteBytes(stringsupport.PaddedString(m.SenderName, 21, true)) return nil } +// Opcode returns the ID associated with this packet type. func (m MsgBinMailNotify) Opcode() network.PacketID { return network.MSG_SYS_CASTED_BINARY } diff --git a/network/clientctx/doc.go b/network/clientctx/doc.go new file mode 100644 index 000000000..90dc9f0e0 --- /dev/null +++ b/network/clientctx/doc.go @@ -0,0 +1,3 @@ +// Package clientctx provides per-connection context passed to packet +// Parse/Build methods, allowing version-dependent encoding decisions. +package clientctx diff --git a/network/crypto/doc.go b/network/crypto/doc.go new file mode 100644 index 000000000..0f91dca60 --- /dev/null +++ b/network/crypto/doc.go @@ -0,0 +1,5 @@ +// Package crypto implements the symmetric substitution-cipher used by Monster +// Hunter Frontier to encrypt and decrypt TCP packet bodies. The algorithm uses +// a 256-byte S-box with a rolling derived key and produces three integrity +// checksums alongside the ciphertext. +package crypto diff --git a/network/doc.go b/network/doc.go new file mode 100644 index 000000000..cb09c243c --- /dev/null +++ b/network/doc.go @@ -0,0 +1,5 @@ +// Package network defines the encrypted TCP transport layer for MHF client +// connections. It provides Blowfish-based packet encryption/decryption via +// [CryptConn], packet header parsing, and the [PacketID] enumeration of all +// ~400 message types in the MHF protocol. +package network diff --git a/network/mhfpacket/doc.go b/network/mhfpacket/doc.go new file mode 100644 index 000000000..5787a3952 --- /dev/null +++ b/network/mhfpacket/doc.go @@ -0,0 +1,4 @@ +// Package mhfpacket defines the struct representations and binary +// serialization for every MHF network packet (~400 message types). Each +// packet implements the [MHFPacket] interface (Parse, Build, Opcode). +package mhfpacket diff --git a/network/mhfpacket/msg_mhf_enumerate_guild.go b/network/mhfpacket/msg_mhf_enumerate_guild.go index 61ead7870..8a6dd6d3a 100644 --- a/network/mhfpacket/msg_mhf_enumerate_guild.go +++ b/network/mhfpacket/msg_mhf_enumerate_guild.go @@ -7,6 +7,7 @@ import ( "erupe-ce/network/clientctx" ) +// EnumerateGuildType specifies the search/sort criteria for guild enumeration. type EnumerateGuildType uint8 const ( diff --git a/network/mhfpacket/msg_mhf_operate_guild.go b/network/mhfpacket/msg_mhf_operate_guild.go index 96803e89c..79c068d73 100644 --- a/network/mhfpacket/msg_mhf_operate_guild.go +++ b/network/mhfpacket/msg_mhf_operate_guild.go @@ -8,6 +8,7 @@ import ( "erupe-ce/network/clientctx" ) +// OperateGuildAction identifies the guild management action to perform. type OperateGuildAction uint8 const ( diff --git a/network/mhfpacket/msg_mhf_operate_guild_member.go b/network/mhfpacket/msg_mhf_operate_guild_member.go index 8daf82dc5..738f7951a 100644 --- a/network/mhfpacket/msg_mhf_operate_guild_member.go +++ b/network/mhfpacket/msg_mhf_operate_guild_member.go @@ -8,6 +8,7 @@ import ( "erupe-ce/network/clientctx" ) +// OperateGuildMemberAction identifies the guild member management action. type OperateGuildMemberAction uint8 const ( diff --git a/network/mhfpacket/msg_mhf_operate_joint.go b/network/mhfpacket/msg_mhf_operate_joint.go index eccb3139d..d818ed8b5 100644 --- a/network/mhfpacket/msg_mhf_operate_joint.go +++ b/network/mhfpacket/msg_mhf_operate_joint.go @@ -8,6 +8,7 @@ import ( "erupe-ce/network/clientctx" ) +// OperateJointAction identifies the alliance (joint) operation to perform. type OperateJointAction uint8 const ( diff --git a/network/mhfpacket/msg_mhf_oprt_mail.go b/network/mhfpacket/msg_mhf_oprt_mail.go index 95ec561ee..8050785f5 100644 --- a/network/mhfpacket/msg_mhf_oprt_mail.go +++ b/network/mhfpacket/msg_mhf_oprt_mail.go @@ -8,6 +8,7 @@ import ( "erupe-ce/network/clientctx" ) +// OperateMailOperation identifies the mail operation to perform. type OperateMailOperation uint8 const ( diff --git a/network/mhfpacket/msg_mhf_set_ca_achievement_hist.go b/network/mhfpacket/msg_mhf_set_ca_achievement_hist.go index 0b4fad343..888d73403 100644 --- a/network/mhfpacket/msg_mhf_set_ca_achievement_hist.go +++ b/network/mhfpacket/msg_mhf_set_ca_achievement_hist.go @@ -8,6 +8,7 @@ import ( "erupe-ce/network/clientctx" ) +// CaAchievementHist is a single entry in the CA achievement history packet. type CaAchievementHist struct { Unk0 uint32 Unk1 uint8 diff --git a/network/mhfpacket/msg_mhf_update_guacot.go b/network/mhfpacket/msg_mhf_update_guacot.go index 2afcbad5c..73eb980fe 100644 --- a/network/mhfpacket/msg_mhf_update_guacot.go +++ b/network/mhfpacket/msg_mhf_update_guacot.go @@ -7,6 +7,7 @@ import ( "erupe-ce/network/clientctx" ) +// Goocoo represents a single Goocoo (guacot) companion entry in an update packet. type Goocoo struct { Index uint32 Data1 []int16 diff --git a/network/mhfpacket/msg_mhf_update_guild_icon.go b/network/mhfpacket/msg_mhf_update_guild_icon.go index 248bb93ea..a53991999 100644 --- a/network/mhfpacket/msg_mhf_update_guild_icon.go +++ b/network/mhfpacket/msg_mhf_update_guild_icon.go @@ -8,6 +8,7 @@ import ( "erupe-ce/network/clientctx" ) +// GuildIconMsgPart represents one graphical part of a guild icon (emblem). type GuildIconMsgPart struct { Index uint16 ID uint16 diff --git a/network/packetid.go b/network/packetid.go index e20ad1970..fa35d5f4a 100644 --- a/network/packetid.go +++ b/network/packetid.go @@ -1,6 +1,8 @@ package network //revive:disable + +// PacketID identifies an MHF network message type. type PacketID uint16 //go:generate stringer -type=PacketID diff --git a/server/api/api_server.go b/server/api/api_server.go index 3774f3fb8..75f31e342 100644 --- a/server/api/api_server.go +++ b/server/api/api_server.go @@ -15,6 +15,7 @@ import ( "go.uber.org/zap" ) +// Config holds the dependencies required to initialize an APIServer. type Config struct { Logger *zap.Logger DB *sqlx.DB diff --git a/server/api/doc.go b/server/api/doc.go new file mode 100644 index 000000000..6818f4155 --- /dev/null +++ b/server/api/doc.go @@ -0,0 +1,5 @@ +// Package api provides an HTTP REST API server (port 8080) for the V2 +// sign/patch flow and administrative endpoints. It handles user +// authentication, character management, launcher configuration, and +// screenshot uploads via JSON and XML over HTTP. +package api diff --git a/server/api/endpoints.go b/server/api/endpoints.go index 96b0d61e6..b114abf2f 100644 --- a/server/api/endpoints.go +++ b/server/api/endpoints.go @@ -24,23 +24,30 @@ import ( "golang.org/x/crypto/bcrypt" ) +// Notification type constants for launcher messages. const ( + // NotificationDefault represents a standard notification. NotificationDefault = iota + // NotificationNew represents a new/unread notification. NotificationNew ) +// LauncherResponse is the JSON payload returned by the /launcher endpoint, +// containing banners, messages, and links for the game launcher UI. type LauncherResponse struct { Banners []_config.APISignBanner `json:"banners"` Messages []_config.APISignMessage `json:"messages"` Links []_config.APISignLink `json:"links"` } +// User represents an authenticated user's session credentials and permissions. type User struct { TokenID uint32 `json:"tokenId"` Token string `json:"token"` Rights uint32 `json:"rights"` } +// Character represents a player character's summary data as returned by the API. type Character struct { ID uint32 `json:"id"` Name string `json:"name"` @@ -51,6 +58,7 @@ type Character struct { LastLogin int32 `json:"lastLogin" db:"last_login"` } +// MezFes represents the current Mezeporta Festival event schedule and ticket configuration. type MezFes struct { ID uint32 `json:"id"` Start uint32 `json:"start"` @@ -60,6 +68,8 @@ type MezFes struct { Stalls []uint32 `json:"stalls"` } +// AuthData is the JSON payload returned after successful login or registration, +// containing session info, character list, event data, and server notices. type AuthData struct { CurrentTS uint32 `json:"currentTs"` ExpiryTS uint32 `json:"expiryTs"` @@ -71,6 +81,7 @@ type AuthData struct { PatchServer string `json:"patchServer"` } +// ExportData wraps a character's full database row for save export. type ExportData struct { Character map[string]interface{} `json:"character"` } @@ -112,6 +123,7 @@ func (s *APIServer) newAuthData(userID uint32, userRights uint32, userTokenID ui return resp } +// Launcher handles GET /launcher and returns banners, messages, and links for the launcher UI. func (s *APIServer) Launcher(w http.ResponseWriter, r *http.Request) { var respData LauncherResponse respData.Banners = s.erupeConfig.API.Banners @@ -121,6 +133,8 @@ func (s *APIServer) Launcher(w http.ResponseWriter, r *http.Request) { _ = json.NewEncoder(w).Encode(respData) } +// Login handles POST /login, authenticating a user by username and password +// and returning a session token with character data. func (s *APIServer) Login(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var reqData struct { @@ -173,6 +187,8 @@ func (s *APIServer) Login(w http.ResponseWriter, r *http.Request) { _ = json.NewEncoder(w).Encode(respData) } +// Register handles POST /register, creating a new user account and returning +// a session token. func (s *APIServer) Register(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var reqData struct { @@ -213,6 +229,8 @@ func (s *APIServer) Register(w http.ResponseWriter, r *http.Request) { _ = json.NewEncoder(w).Encode(respData) } +// CreateCharacter handles POST /character/create, creating a new character +// slot for the authenticated user. func (s *APIServer) CreateCharacter(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var reqData struct { @@ -242,6 +260,8 @@ func (s *APIServer) CreateCharacter(w http.ResponseWriter, r *http.Request) { _ = json.NewEncoder(w).Encode(character) } +// DeleteCharacter handles POST /character/delete, soft-deleting an existing +// character or removing an unfinished one. func (s *APIServer) DeleteCharacter(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var reqData struct { @@ -267,6 +287,8 @@ func (s *APIServer) DeleteCharacter(w http.ResponseWriter, r *http.Request) { _ = json.NewEncoder(w).Encode(struct{}{}) } +// ExportSave handles POST /character/export, returning the full character +// database row as JSON for backup purposes. func (s *APIServer) ExportSave(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var reqData struct { @@ -295,6 +317,8 @@ func (s *APIServer) ExportSave(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(save) } +// ScreenShotGet handles GET /api/ss/bbs/{id}, serving a previously uploaded +// screenshot image by its token ID. func (s *APIServer) ScreenShotGet(w http.ResponseWriter, r *http.Request) { // Get the 'id' parameter from the URL token := mux.Vars(r)["id"] @@ -329,6 +353,8 @@ func (s *APIServer) ScreenShotGet(w http.ResponseWriter, r *http.Request) { } } } +// ScreenShot handles POST /api/ss/bbs/upload.php, accepting a JPEG image +// upload from the game client and saving it to the configured output directory. func (s *APIServer) ScreenShot(w http.ResponseWriter, r *http.Request) { type Result struct { XMLName xml.Name `xml:"result"` diff --git a/server/channelserver/compression/deltacomp/doc.go b/server/channelserver/compression/deltacomp/doc.go new file mode 100644 index 000000000..c24e56616 --- /dev/null +++ b/server/channelserver/compression/deltacomp/doc.go @@ -0,0 +1,3 @@ +// Package deltacomp implements delta-diff decompression for incremental save +// data updates sent by the MHF client. +package deltacomp diff --git a/server/channelserver/compression/nullcomp/doc.go b/server/channelserver/compression/nullcomp/doc.go new file mode 100644 index 000000000..ee852ed3a --- /dev/null +++ b/server/channelserver/compression/nullcomp/doc.go @@ -0,0 +1,4 @@ +// Package nullcomp implements null-byte run-length compression used by the MHF +// client for save data. The format uses a "cmp 20110113" header and encodes +// runs of zero bytes as a (0x00, count) pair. +package nullcomp diff --git a/server/channelserver/doc.go b/server/channelserver/doc.go new file mode 100644 index 000000000..e44f2a699 --- /dev/null +++ b/server/channelserver/doc.go @@ -0,0 +1,11 @@ +// Package channelserver implements the gameplay channel server (TCP port +// 54001+) that handles all in-game multiplayer functionality. It manages +// player sessions, stage (lobby/quest room) state, guild operations, item +// management, event systems, and binary state relay between clients. +// +// Packet handlers are organized by game system into separate files +// (handlers_quest.go, handlers_guild.go, etc.) and registered in +// handlers_table.go. Each handler has the signature: +// +// func(s *Session, p mhfpacket.MHFPacket) +package channelserver diff --git a/server/channelserver/guild_model.go b/server/channelserver/guild_model.go index d484f4ce6..ba6988b4d 100644 --- a/server/channelserver/guild_model.go +++ b/server/channelserver/guild_model.go @@ -15,6 +15,7 @@ import ( "go.uber.org/zap" ) +// FestivalColor is a festival color identifier string. type FestivalColor string const ( @@ -23,12 +24,14 @@ const ( FestivalColorRed FestivalColor = "red" ) +// FestivalColorCodes maps festival colors to their numeric codes. var FestivalColorCodes = map[FestivalColor]int16{ FestivalColorNone: -1, FestivalColorBlue: 0, FestivalColorRed: 1, } +// GuildApplicationType is the type of a guild application (applied or invited). type GuildApplicationType string const ( @@ -36,6 +39,7 @@ const ( GuildApplicationTypeInvited GuildApplicationType = "invited" ) +// Guild represents a guild with all its metadata. type Guild struct { ID uint32 `db:"id"` Name string `db:"name"` @@ -64,11 +68,13 @@ type Guild struct { GuildLeader } +// GuildLeader holds the character ID and name of a guild's leader. type GuildLeader struct { LeaderCharID uint32 `db:"leader_id"` LeaderName string `db:"leader_name"` } +// GuildIconPart represents one graphical part of a guild icon. type GuildIconPart struct { Index uint16 ID uint16 @@ -82,6 +88,7 @@ type GuildIconPart struct { PosY uint16 } +// GuildApplication represents a pending guild application or invitation. type GuildApplication struct { ID int `db:"id"` GuildID uint32 `db:"guild_id"` @@ -91,6 +98,7 @@ type GuildApplication struct { CreatedAt time.Time `db:"created_at"` } +// GuildIcon is a composite guild icon made up of multiple parts. type GuildIcon struct { Parts []GuildIconPart } @@ -467,6 +475,7 @@ func (guild *Guild) HasApplicationForCharID(s *Session, charID uint32) (bool, er return true, nil } +// CreateGuild creates a new guild in the database and adds the session's character as its leader. func CreateGuild(s *Session, guildName string) (int32, error) { transaction, err := s.server.db.Begin() @@ -539,6 +548,7 @@ func rollbackTransaction(s *Session, transaction *sql.Tx) { } } +// GetGuildInfoByID retrieves guild info by guild ID, returning nil if not found. func GetGuildInfoByID(s *Session, guildID uint32) (*Guild, error) { rows, err := s.server.db.Queryx(fmt.Sprintf(` %s @@ -562,6 +572,7 @@ func GetGuildInfoByID(s *Session, guildID uint32) (*Guild, error) { return buildGuildObjectFromDbResult(rows, err, s) } +// GetGuildInfoByCharacterId retrieves guild info for a character, including applied guilds. func GetGuildInfoByCharacterId(s *Session, charID uint32) (*Guild, error) { rows, err := s.server.db.Queryx(fmt.Sprintf(` %s diff --git a/server/channelserver/handlers_achievement.go b/server/channelserver/handlers_achievement.go index 69d5782ad..d6ab68edb 100644 --- a/server/channelserver/handlers_achievement.go +++ b/server/channelserver/handlers_achievement.go @@ -32,6 +32,7 @@ var achievementCurveMap = map[uint8][]int32{ 32: achievementCurves[3], } +// Achievement represents computed achievement data for a character. type Achievement struct { Level uint8 Value uint32 @@ -42,6 +43,7 @@ type Achievement struct { Trophy uint8 } +// GetAchData computes achievement level and progress from a raw score. func GetAchData(id uint8, score int32) Achievement { curve := achievementCurveMap[id] var ach Achievement diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index bcfa348d0..b283d4f1c 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -109,6 +109,7 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } +// CafeBonus represents a cafe duration bonus reward entry. type CafeBonus struct { ID uint32 `db:"id"` TimeReq uint32 `db:"time_req"` diff --git a/server/channelserver/handlers_campaign.go b/server/channelserver/handlers_campaign.go index ed8fc7c3e..df3079617 100644 --- a/server/channelserver/handlers_campaign.go +++ b/server/channelserver/handlers_campaign.go @@ -9,6 +9,7 @@ import ( "time" ) +// CampaignEvent represents a promotional campaign event. type CampaignEvent struct { ID uint32 Unk0 uint32 @@ -35,6 +36,7 @@ type CampaignEvent struct { Categories []uint16 } +// CampaignCategory represents a category grouping for campaign events. type CampaignCategory struct { ID uint16 Type uint8 @@ -42,6 +44,7 @@ type CampaignCategory struct { Description string } +// CampaignLink links a campaign event to its items/rewards. type CampaignLink struct { CategoryID uint16 CampaignID uint32 diff --git a/server/channelserver/handlers_caravan.go b/server/channelserver/handlers_caravan.go index c90f44812..e54c8ae9d 100644 --- a/server/channelserver/handlers_caravan.go +++ b/server/channelserver/handlers_caravan.go @@ -7,6 +7,7 @@ import ( "time" ) +// RyoudamaReward represents a caravan (Ryoudama) reward entry. type RyoudamaReward struct { Unk0 uint8 Unk1 uint8 @@ -16,22 +17,26 @@ type RyoudamaReward struct { Unk5 uint16 } +// RyoudamaKeyScore represents a caravan key score entry. type RyoudamaKeyScore struct { Unk0 uint8 Unk1 int32 } +// RyoudamaCharInfo represents per-character caravan info. type RyoudamaCharInfo struct { CID uint32 Unk0 int32 Name string } +// RyoudamaBoostInfo represents caravan boost status. type RyoudamaBoostInfo struct { Start time.Time End time.Time } +// Ryoudama represents complete caravan data. type Ryoudama struct { Reward []RyoudamaReward KeyScore []RyoudamaKeyScore diff --git a/server/channelserver/handlers_character.go b/server/channelserver/handlers_character.go index d16fd5223..c5558e839 100644 --- a/server/channelserver/handlers_character.go +++ b/server/channelserver/handlers_character.go @@ -9,6 +9,7 @@ import ( "go.uber.org/zap" ) +// GetCharacterSaveData loads a character's save data from the database. func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error) { result, err := s.server.db.Query("SELECT id, savedata, is_new_character, name FROM characters WHERE id = $1", charID) if err != nil { diff --git a/server/channelserver/handlers_data_paper.go b/server/channelserver/handlers_data_paper.go index b8015c3e3..214e01dcf 100644 --- a/server/channelserver/handlers_data_paper.go +++ b/server/channelserver/handlers_data_paper.go @@ -10,11 +10,13 @@ import ( "go.uber.org/zap" ) +// PaperMissionTimetable represents a daily mission schedule entry. type PaperMissionTimetable struct { Start time.Time End time.Time } +// PaperMissionData represents daily mission details. type PaperMissionData struct { Unk0 uint8 Unk1 uint8 @@ -25,11 +27,13 @@ type PaperMissionData struct { Reward2Quantity uint8 } +// PaperMission represents a daily mission wrapper. type PaperMission struct { Timetables []PaperMissionTimetable Data []PaperMissionData } +// PaperData represents complete daily paper data. type PaperData struct { Unk0 uint16 Unk1 int16 @@ -40,6 +44,7 @@ type PaperData struct { Unk6 int16 } +// PaperGift represents a paper gift reward entry. type PaperGift struct { Unk0 uint16 Unk1 uint8 diff --git a/server/channelserver/handlers_distitem.go b/server/channelserver/handlers_distitem.go index f407d1981..8c10a648d 100644 --- a/server/channelserver/handlers_distitem.go +++ b/server/channelserver/handlers_distitem.go @@ -10,6 +10,7 @@ import ( "go.uber.org/zap" ) +// Distribution represents an item distribution event. type Distribution struct { ID uint32 `db:"id"` Deadline time.Time `db:"deadline"` @@ -119,6 +120,7 @@ func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } +// DistributionItem represents a single item in a distribution. type DistributionItem struct { ItemType uint8 `db:"item_type"` ID uint32 `db:"id"` diff --git a/server/channelserver/handlers_event.go b/server/channelserver/handlers_event.go index 71d08eac5..12751ee40 100644 --- a/server/channelserver/handlers_event.go +++ b/server/channelserver/handlers_event.go @@ -11,6 +11,7 @@ import ( "go.uber.org/zap" ) +// Event represents an in-game event entry. type Event struct { EventType uint16 Unk1 uint16 diff --git a/server/channelserver/handlers_gacha.go b/server/channelserver/handlers_gacha.go index 01344872b..977f64fdb 100644 --- a/server/channelserver/handlers_gacha.go +++ b/server/channelserver/handlers_gacha.go @@ -8,6 +8,7 @@ import ( "go.uber.org/zap" ) +// Gacha represents a gacha lottery definition. type Gacha struct { ID uint32 `db:"id"` MinGR uint32 `db:"min_gr"` @@ -22,6 +23,7 @@ type Gacha struct { Hidden bool `db:"hidden"` } +// GachaEntry represents a gacha entry (step/box). type GachaEntry struct { EntryType uint8 `db:"entry_type"` ID uint32 `db:"id"` @@ -36,6 +38,7 @@ type GachaEntry struct { Name string `db:"name"` } +// GachaItem represents a single item in a gacha pool. type GachaItem struct { ItemType uint8 `db:"item_type"` ItemID uint16 `db:"item_id"` diff --git a/server/channelserver/handlers_guild_adventure.go b/server/channelserver/handlers_guild_adventure.go index 8e953bf24..2ee1a76ef 100644 --- a/server/channelserver/handlers_guild_adventure.go +++ b/server/channelserver/handlers_guild_adventure.go @@ -9,6 +9,7 @@ import ( "go.uber.org/zap" ) +// GuildAdventure represents a guild adventure expedition. type GuildAdventure struct { ID uint32 `db:"id"` Destination uint32 `db:"destination"` diff --git a/server/channelserver/handlers_guild_alliance.go b/server/channelserver/handlers_guild_alliance.go index 4ac09f62e..e52cb1164 100644 --- a/server/channelserver/handlers_guild_alliance.go +++ b/server/channelserver/handlers_guild_alliance.go @@ -28,6 +28,7 @@ END FROM guild_alliances ga ` +// GuildAlliance represents a multi-guild alliance. type GuildAlliance struct { ID uint32 `db:"id"` Name string `db:"name"` @@ -43,6 +44,7 @@ type GuildAlliance struct { SubGuild2 Guild } +// GetAllianceData loads alliance data from the database. func GetAllianceData(s *Session, AllianceID uint32) (*GuildAlliance, error) { rows, err := s.server.db.Queryx(fmt.Sprintf(` %s diff --git a/server/channelserver/handlers_guild_board.go b/server/channelserver/handlers_guild_board.go index c5da602fe..7530dad8d 100644 --- a/server/channelserver/handlers_guild_board.go +++ b/server/channelserver/handlers_guild_board.go @@ -10,6 +10,7 @@ import ( "go.uber.org/zap" ) +// MessageBoardPost represents a guild message board post. type MessageBoardPost struct { ID uint32 `db:"id"` StampID uint32 `db:"stamp_id"` diff --git a/server/channelserver/handlers_guild_cooking.go b/server/channelserver/handlers_guild_cooking.go index 13d3c7f4c..cfe87fba0 100644 --- a/server/channelserver/handlers_guild_cooking.go +++ b/server/channelserver/handlers_guild_cooking.go @@ -8,6 +8,7 @@ import ( "go.uber.org/zap" ) +// GuildMeal represents a guild cooking meal entry. type GuildMeal struct { ID uint32 `db:"id"` MealID uint32 `db:"meal_id"` diff --git a/server/channelserver/handlers_guild_member.go b/server/channelserver/handlers_guild_member.go index e11faf4d2..09a63ee9a 100644 --- a/server/channelserver/handlers_guild_member.go +++ b/server/channelserver/handlers_guild_member.go @@ -8,6 +8,7 @@ import ( "go.uber.org/zap" ) +// GuildMember represents a guild member with role and stats. type GuildMember struct { GuildID uint32 `db:"guild_id"` CharID uint32 `db:"character_id"` @@ -92,6 +93,7 @@ SELECT LEFT JOIN guilds g ON g.id = gc.guild_id ` +// GetGuildMembers loads all members of a guild. func GetGuildMembers(s *Session, guildID uint32, applicants bool) ([]*GuildMember, error) { rows, err := s.server.db.Queryx(fmt.Sprintf(` %s @@ -120,6 +122,7 @@ func GetGuildMembers(s *Session, guildID uint32, applicants bool) ([]*GuildMembe return members, nil } +// GetCharacterGuildData loads a character's guild membership. func GetCharacterGuildData(s *Session, charID uint32) (*GuildMember, error) { rows, err := s.server.db.Queryx(fmt.Sprintf("%s WHERE character.character_id=$1", guildMembersSelectSQL), charID) diff --git a/server/channelserver/handlers_guild_mission.go b/server/channelserver/handlers_guild_mission.go index 53aa59dbd..76081d484 100644 --- a/server/channelserver/handlers_guild_mission.go +++ b/server/channelserver/handlers_guild_mission.go @@ -5,6 +5,7 @@ import ( "erupe-ce/network/mhfpacket" ) +// GuildMission represents a guild mission entry. type GuildMission struct { ID uint32 Unk uint32 diff --git a/server/channelserver/handlers_guild_tresure.go b/server/channelserver/handlers_guild_tresure.go index b5896c7ab..6a65ad369 100644 --- a/server/channelserver/handlers_guild_tresure.go +++ b/server/channelserver/handlers_guild_tresure.go @@ -9,6 +9,7 @@ import ( "go.uber.org/zap" ) +// TreasureHunt represents a guild treasure hunt entry. type TreasureHunt struct { HuntID uint32 `db:"id"` HostID uint32 `db:"host_id"` @@ -147,6 +148,7 @@ func handleMsgMhfOperateGuildTresureReport(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } +// TreasureSouvenir represents a guild treasure souvenir entry. type TreasureSouvenir struct { Destination uint32 Quantity uint32 diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index ebcb1b6bc..74e5510c3 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -47,6 +47,7 @@ func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } +// HouseData represents player house/my house data. type HouseData struct { CharID uint32 `db:"id"` HR uint16 `db:"hr"` @@ -329,6 +330,7 @@ func handleMsgMhfSaveDecoMyset(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } +// Title represents a hunter title entry. type Title struct { ID uint16 `db:"id"` Acquired time.Time `db:"unlocked_at"` diff --git a/server/channelserver/handlers_mail.go b/server/channelserver/handlers_mail.go index e5966067d..d226ba6ea 100644 --- a/server/channelserver/handlers_mail.go +++ b/server/channelserver/handlers_mail.go @@ -11,6 +11,7 @@ import ( "go.uber.org/zap" ) +// Mail represents an in-game mail message. type Mail struct { ID int `db:"id"` SenderID uint32 `db:"sender_id"` @@ -79,6 +80,7 @@ func (m *Mail) MarkRead(s *Session) error { return nil } +// GetMailListForCharacter loads all mail for a character. func GetMailListForCharacter(s *Session, charID uint32) ([]Mail, error) { rows, err := s.server.db.Queryx(` SELECT @@ -127,6 +129,7 @@ func GetMailListForCharacter(s *Session, charID uint32) ([]Mail, error) { return allMail, nil } +// GetMailByID loads a single mail by ID. func GetMailByID(s *Session, ID int) (*Mail, error) { row := s.server.db.QueryRowx(` SELECT @@ -167,6 +170,7 @@ func GetMailByID(s *Session, ID int) (*Mail, error) { return mail, nil } +// SendMailNotification sends a new mail notification to a player. func SendMailNotification(s *Session, m *Mail, recipient *Session) { bf := byteframe.NewByteFrame() diff --git a/server/channelserver/handlers_mercenary.go b/server/channelserver/handlers_mercenary.go index 67b19059f..08d15c25d 100644 --- a/server/channelserver/handlers_mercenary.go +++ b/server/channelserver/handlers_mercenary.go @@ -370,6 +370,7 @@ func handleMsgMhfEnumerateAiroulist(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } +// Airou represents Airou (felyne companion) data. type Airou struct { ID uint32 Name []byte @@ -445,6 +446,7 @@ func getGuildAirouList(s *Session) []Airou { return guildCats } +// GetAirouDetails parses Airou data from a ByteFrame. func GetAirouDetails(bf *byteframe.ByteFrame) []Airou { catCount := bf.ReadUint8() cats := make([]Airou, catCount) diff --git a/server/channelserver/handlers_misc.go b/server/channelserver/handlers_misc.go index 9bb830356..c8160dc64 100644 --- a/server/channelserver/handlers_misc.go +++ b/server/channelserver/handlers_misc.go @@ -241,6 +241,7 @@ func handleMsgMhfGetLobbyCrowd(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0x320)) } +// TrendWeapon represents trending weapon usage data. type TrendWeapon struct { WeaponType uint8 WeaponID uint16 diff --git a/server/channelserver/handlers_quest.go b/server/channelserver/handlers_quest.go index 1eb8408ba..fa5e30dc2 100644 --- a/server/channelserver/handlers_quest.go +++ b/server/channelserver/handlers_quest.go @@ -48,6 +48,7 @@ func equal(a, b []byte) bool { return true } +// BackportQuest converts a quest binary to an older format. func BackportQuest(data []byte) []byte { wp := binary.LittleEndian.Uint32(data[0:4]) + 96 rp := wp + 4 diff --git a/server/channelserver/handlers_register.go b/server/channelserver/handlers_register.go index fbb1e8680..ebeb6c892 100644 --- a/server/channelserver/handlers_register.go +++ b/server/channelserver/handlers_register.go @@ -48,6 +48,7 @@ func handleMsgMhfReleaseEvent(s *Session, p mhfpacket.MHFPacket) { }) } +// RaviUpdate represents a Raviente register update entry. type RaviUpdate struct { Op uint8 Dest uint8 diff --git a/server/channelserver/handlers_rengoku.go b/server/channelserver/handlers_rengoku.go index 2f02601ef..0a8f9c573 100644 --- a/server/channelserver/handlers_rengoku.go +++ b/server/channelserver/handlers_rengoku.go @@ -107,6 +107,7 @@ const rengokuScoreQuery = `, c.name FROM rengoku_score rs LEFT JOIN characters c ON c.id = rs.character_id LEFT JOIN guild_characters gc ON gc.character_id = rs.character_id ` +// RengokuScore represents a Rengoku (Hunting Road) ranking score. type RengokuScore struct { Name string `db:"name"` Score uint32 `db:"score"` diff --git a/server/channelserver/handlers_scenario.go b/server/channelserver/handlers_scenario.go index 5ab10c3cb..307afa3a5 100644 --- a/server/channelserver/handlers_scenario.go +++ b/server/channelserver/handlers_scenario.go @@ -7,6 +7,7 @@ import ( "go.uber.org/zap" ) +// Scenario represents scenario counter data. type Scenario struct { MainID uint32 // 0 = Basic diff --git a/server/channelserver/handlers_seibattle.go b/server/channelserver/handlers_seibattle.go index f8d6c8aa8..758338a73 100644 --- a/server/channelserver/handlers_seibattle.go +++ b/server/channelserver/handlers_seibattle.go @@ -6,27 +6,32 @@ import ( "time" ) +// SeibattleTimetable represents a seibattle schedule entry. type SeibattleTimetable struct { Start time.Time End time.Time } +// SeibattleKeyScore represents a seibattle key score. type SeibattleKeyScore struct { Unk0 uint8 Unk1 int32 } +// SeibattleCareer represents seibattle career stats. type SeibattleCareer struct { Unk0 uint16 Unk1 uint16 Unk2 uint16 } +// SeibattleOpponent represents seibattle opponent data. type SeibattleOpponent struct { Unk0 int32 Unk1 int8 } +// SeibattleConventionResult represents a seibattle convention result. type SeibattleConventionResult struct { Unk0 uint32 Unk1 uint16 @@ -35,10 +40,12 @@ type SeibattleConventionResult struct { Unk4 uint16 } +// SeibattleCharScore represents a seibattle per-character score. type SeibattleCharScore struct { Unk0 uint32 } +// SeibattleCurResult represents a seibattle current result. type SeibattleCurResult struct { Unk0 uint32 Unk1 uint16 @@ -46,6 +53,7 @@ type SeibattleCurResult struct { Unk3 uint16 } +// Seibattle represents complete seibattle data. type Seibattle struct { Timetable []SeibattleTimetable KeyScore []SeibattleKeyScore @@ -159,6 +167,7 @@ func handleMsgMhfGetBreakSeibatuLevelReward(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } +// WeeklySeibatuRankingReward represents a weekly seibattle ranking reward. type WeeklySeibatuRankingReward struct { Unk0 int32 Unk1 int32 diff --git a/server/channelserver/handlers_shop.go b/server/channelserver/handlers_shop.go index 10d3919ea..2b1d3e5e6 100644 --- a/server/channelserver/handlers_shop.go +++ b/server/channelserver/handlers_shop.go @@ -9,6 +9,7 @@ import ( "go.uber.org/zap" ) +// ShopItem represents a shop item listing. type ShopItem struct { ID uint32 `db:"id"` ItemID uint32 `db:"item_id"` @@ -248,6 +249,7 @@ func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } +// FPointExchange represents a frontier point exchange entry. type FPointExchange struct { ID uint32 `db:"id"` ItemType uint8 `db:"item_type"` diff --git a/server/channelserver/handlers_tournament.go b/server/channelserver/handlers_tournament.go index 87fc95330..0d356fe84 100644 --- a/server/channelserver/handlers_tournament.go +++ b/server/channelserver/handlers_tournament.go @@ -7,6 +7,7 @@ import ( "time" ) +// TournamentInfo0 represents tournament information (type 0). type TournamentInfo0 struct { ID uint32 MaxPlayers uint32 @@ -28,6 +29,7 @@ type TournamentInfo0 struct { Unk6 string } +// TournamentInfo21 represents tournament information (type 21). type TournamentInfo21 struct { Unk0 uint32 Unk1 uint32 @@ -35,6 +37,7 @@ type TournamentInfo21 struct { Unk3 uint8 } +// TournamentInfo22 represents tournament information (type 22). type TournamentInfo22 struct { Unk0 uint32 Unk1 uint32 @@ -110,6 +113,7 @@ func handleMsgMhfEntryTournament(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } +// TournamentReward represents a tournament reward entry. type TournamentReward struct { Unk0 uint16 Unk1 uint16 diff --git a/server/channelserver/handlers_tower.go b/server/channelserver/handlers_tower.go index c48d9f1fc..ad51cdaea 100644 --- a/server/channelserver/handlers_tower.go +++ b/server/channelserver/handlers_tower.go @@ -14,21 +14,25 @@ import ( "erupe-ce/network/mhfpacket" ) +// TowerInfoTRP represents tower RP (points) info. type TowerInfoTRP struct { TR int32 TRP int32 } +// TowerInfoSkill represents tower skill info. type TowerInfoSkill struct { TSP int32 Skills []int16 // 64 } +// TowerInfoHistory represents tower clear history. type TowerInfoHistory struct { Unk0 []int16 // 5 Unk1 []int16 // 5 } +// TowerInfoLevel represents tower level info. type TowerInfoLevel struct { Floors int32 Unk1 int32 @@ -36,6 +40,7 @@ type TowerInfoLevel struct { Unk3 int32 } +// EmptyTowerCSV creates an empty CSV string of the given length. func EmptyTowerCSV(len int) string { temp := make([]string, len) for i := range temp { @@ -194,6 +199,7 @@ var tenrouiraiData = []TenrouiraiData{ {2, 6, 40, 0, 3, 1, 0, 0, 1, 1}, } +// TenrouiraiProgress represents Tenrouirai (sky corridor) progress. type TenrouiraiProgress struct { Page uint8 Mission1 uint16 @@ -201,17 +207,20 @@ type TenrouiraiProgress struct { Mission3 uint16 } +// TenrouiraiReward represents a Tenrouirai reward. type TenrouiraiReward struct { Index uint8 Item []uint16 // 5 Quantity []uint8 // 5 } +// TenrouiraiKeyScore represents a Tenrouirai key score. type TenrouiraiKeyScore struct { Unk0 uint8 Unk1 int32 } +// TenrouiraiData represents Tenrouirai data. type TenrouiraiData struct { Block uint8 Mission uint8 @@ -231,17 +240,20 @@ type TenrouiraiData struct { Skill6 uint8 // 50 } +// TenrouiraiCharScore represents a Tenrouirai per-character score. type TenrouiraiCharScore struct { Score int32 Name string } +// TenrouiraiTicket represents a Tenrouirai ticket entry. type TenrouiraiTicket struct { Unk0 uint8 RP uint32 Unk2 uint32 } +// Tenrouirai represents complete Tenrouirai data. type Tenrouirai struct { Progress []TenrouiraiProgress Reward []TenrouiraiReward @@ -429,11 +441,13 @@ func handleMsgMhfPresentBox(s *Session, p mhfpacket.MHFPacket) { doAckEarthSucceed(s, pkt.AckHandle, data) } +// GemInfo represents gem (decoration) info. type GemInfo struct { Gem uint16 Quantity uint16 } +// GemHistory represents gem usage history. type GemHistory struct { Gem uint16 Message uint16 diff --git a/server/channelserver/model_character.go b/server/channelserver/model_character.go index 534994116..a70d59076 100644 --- a/server/channelserver/model_character.go +++ b/server/channelserver/model_character.go @@ -9,6 +9,7 @@ import ( "erupe-ce/server/channelserver/compression/nullcomp" ) +// SavePointer identifies a section within the character save data blob. type SavePointer int const ( @@ -29,6 +30,7 @@ const ( lBookshelfData ) +// CharacterSaveData holds a character's save data and its parsed fields. type CharacterSaveData struct { CharID uint32 Name string diff --git a/server/channelserver/raviente.go b/server/channelserver/raviente.go index 881f962f5..7e4da6a81 100644 --- a/server/channelserver/raviente.go +++ b/server/channelserver/raviente.go @@ -11,6 +11,7 @@ import ( "go.uber.org/zap" ) +// Raviente holds shared state for the Raviente siege event. type Raviente struct { sync.Mutex id uint16 diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index 25c2ea7c8..c4349d2e8 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -264,6 +264,7 @@ func (s *Server) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) } } +// WorldcastMHF broadcasts a packet to all sessions across all channel servers. func (s *Server) WorldcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session, ignoredChannel *Server) { for _, c := range s.Channels { if c == ignoredChannel { @@ -292,6 +293,7 @@ func (s *Server) BroadcastChatMessage(message string) { }, nil) } +// DiscordChannelSend sends a chat message to the configured Discord channel. func (s *Server) DiscordChannelSend(charName string, content string) { if s.erupeConfig.Discord.Enabled && s.discordBot != nil { message := fmt.Sprintf("**%s**: %s", charName, content) @@ -299,6 +301,7 @@ func (s *Server) DiscordChannelSend(charName string, content string) { } } +// DiscordScreenShotSend sends a screenshot link to the configured Discord channel. func (s *Server) DiscordScreenShotSend(charName string, title string, description string, articleToken string) { if s.erupeConfig.Discord.Enabled && s.discordBot != nil { imageUrl := fmt.Sprintf("%s:%d/api/ss/bbs/%s", s.erupeConfig.Screenshots.Host, s.erupeConfig.Screenshots.Port, articleToken) @@ -307,6 +310,7 @@ func (s *Server) DiscordScreenShotSend(charName string, title string, descriptio } } +// FindSessionByCharID looks up a session by character ID across all channels. func (s *Server) FindSessionByCharID(charID uint32) *Session { for _, c := range s.Channels { for _, session := range c.sessions { @@ -318,6 +322,7 @@ func (s *Server) FindSessionByCharID(charID uint32) *Session { return nil } +// DisconnectUser disconnects all sessions belonging to the given user ID. func (s *Server) DisconnectUser(uid uint32) { var cid uint32 var cids []uint32 @@ -343,6 +348,7 @@ func (s *Server) DisconnectUser(uid uint32) { } } +// FindObjectByChar finds a stage object owned by the given character ID. func (s *Server) FindObjectByChar(charID uint32) *Object { s.stagesLock.RLock() defer s.stagesLock.RUnlock() @@ -361,6 +367,7 @@ func (s *Server) FindObjectByChar(charID uint32) *Object { return nil } +// HasSemaphore checks if the given session is hosting any semaphore. func (s *Server) HasSemaphore(ses *Session) bool { for _, semaphore := range s.semaphore { if semaphore.host == ses { @@ -370,6 +377,7 @@ func (s *Server) HasSemaphore(ses *Session) bool { return false } +// Season returns the current in-game season (0-2) based on server ID and time. func (s *Server) Season() uint8 { sid := int64(((s.ID & 0xFF00) - 4096) / 256) return uint8(((TimeAdjusted().Unix() / 86400) + sid) % 3) diff --git a/server/channelserver/sys_session.go b/server/channelserver/sys_session.go index 1e06f5f63..294d470ab 100644 --- a/server/channelserver/sys_session.go +++ b/server/channelserver/sys_session.go @@ -323,6 +323,7 @@ func (s *Session) getObjectId() uint32 { return uint32(s.objectID)<<16 | uint32(s.objectIndex) } +// GetSemaphoreID returns the semaphore ID held by the session, varying by semaphore mode. func (s *Session) GetSemaphoreID() uint32 { if s.semaphoreMode { return 0x000E0000 + uint32(s.semaphoreID[1]) diff --git a/server/channelserver/sys_time.go b/server/channelserver/sys_time.go index 873bd67c6..885bde66c 100644 --- a/server/channelserver/sys_time.go +++ b/server/channelserver/sys_time.go @@ -5,6 +5,11 @@ import ( "time" ) +// TimeAdjusted, TimeMidnight, TimeWeekStart, TimeWeekNext, and TimeGameAbsolute +// are package-level wrappers around the gametime utility functions, providing +// convenient access to adjusted server time, daily/weekly boundaries, and the +// absolute game timestamp used by the MHF client. + func TimeAdjusted() time.Time { return gametime.Adjusted() } func TimeMidnight() time.Time { return gametime.Midnight() } func TimeWeekStart() time.Time { return gametime.WeekStart() } diff --git a/server/discordbot/discord_bot.go b/server/discordbot/discord_bot.go index 303cbc630..b0d809776 100644 --- a/server/discordbot/discord_bot.go +++ b/server/discordbot/discord_bot.go @@ -8,6 +8,8 @@ import ( "go.uber.org/zap" ) +// Commands defines the slash commands registered with Discord, including +// account linking and password management. var Commands = []*discordgo.ApplicationCommand{ { Name: "link", @@ -35,6 +37,8 @@ var Commands = []*discordgo.ApplicationCommand{ }, } +// DiscordBot manages a Discord session and provides methods for relaying +// messages between the game server and a configured Discord channel. type DiscordBot struct { Session *discordgo.Session config *_config.Config @@ -43,11 +47,14 @@ type DiscordBot struct { RelayChannel *discordgo.Channel } +// Options holds the configuration and logger required to create a DiscordBot. type Options struct { Config *_config.Config Logger *zap.Logger } +// NewDiscordBot creates a DiscordBot using the provided options, establishing +// a Discord session and optionally resolving the relay channel. func NewDiscordBot(options Options) (discordBot *DiscordBot, err error) { session, err := discordgo.New("Bot " + options.Config.Discord.BotToken) @@ -77,6 +84,7 @@ func NewDiscordBot(options Options) (discordBot *DiscordBot, err error) { return } +// Start opens the websocket connection to Discord. func (bot *DiscordBot) Start() (err error) { err = bot.Session.Open() @@ -105,6 +113,8 @@ func (bot *DiscordBot) NormalizeDiscordMessage(message string) string { return result } +// RealtimeChannelSend sends a message to the configured relay channel. If no +// relay channel is configured, the call is a no-op. func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) { if bot.RelayChannel == nil { return @@ -114,6 +124,8 @@ func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) { return } +// ReplaceTextAll replaces every match of regex in text by calling handler with +// the first capture group of each match and substituting the result. func ReplaceTextAll(text string, regex *regexp.Regexp, handler func(input string) string) string { result := regex.ReplaceAllFunc([]byte(text), func(s []byte) []byte { input := regex.ReplaceAllString(string(s), `$1`) diff --git a/server/discordbot/doc.go b/server/discordbot/doc.go new file mode 100644 index 000000000..0407c7fd1 --- /dev/null +++ b/server/discordbot/doc.go @@ -0,0 +1,4 @@ +// Package discordbot provides an optional Discord bot integration that relays +// in-game chat to Discord channels and supports slash commands for server +// management. +package discordbot diff --git a/server/entranceserver/doc.go b/server/entranceserver/doc.go new file mode 100644 index 000000000..b788c94fd --- /dev/null +++ b/server/entranceserver/doc.go @@ -0,0 +1,12 @@ +// Package entranceserver implements the MHF entrance server, which listens on +// TCP port 53310 and acts as the gateway between authentication (sign server) +// and gameplay (channel servers). It presents the server list to authenticated +// clients, handles character selection, and directs players to the appropriate +// channel server. +// +// The entrance server uses MHF's custom "binary8" encryption and "sum32" +// checksum for all client-server communication. Each client connection is +// short-lived: the server sends a single response containing the server list +// (SV2/SVR) and optionally user session data (USR), then closes the +// connection. +package entranceserver diff --git a/server/signserver/doc.go b/server/signserver/doc.go new file mode 100644 index 000000000..e3565ef8d --- /dev/null +++ b/server/signserver/doc.go @@ -0,0 +1,5 @@ +// Package signserver implements the MHF sign server, which handles client +// authentication, session creation, and character management. It listens +// on TCP port 53312 and is the first server a client connects to in the +// three-server network model (sign, entrance, channel). +package signserver diff --git a/server/signserver/respid.go b/server/signserver/respid.go index 014daa862..ece8a03da 100644 --- a/server/signserver/respid.go +++ b/server/signserver/respid.go @@ -1,7 +1,11 @@ package signserver +// RespID represents a sign server response code sent to the client +// to indicate the result of an authentication or session operation. type RespID uint8 +// Sign server response codes. These values are sent as the first byte of +// a sign response and map to client-side error messages. const ( SIGN_UNKNOWN RespID = iota SIGN_SUCCESS