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/.
This commit is contained in:
Houmgaor
2026-02-18 21:39:13 +01:00
parent b9cb274ced
commit 2bd5f98f32
81 changed files with 342 additions and 0 deletions

3
common/bfutil/doc.go Normal file
View File

@@ -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

View File

@@ -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
}

4
common/byteframe/doc.go Normal file
View File

@@ -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

4
common/decryption/doc.go Normal file
View File

@@ -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

View File

@@ -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

4
common/gametime/doc.go Normal file
View File

@@ -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

View File

@@ -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)
}

3
common/mhfcid/doc.go Normal file
View File

@@ -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

5
common/mhfcourse/doc.go Normal file
View File

@@ -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

View File

@@ -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)))
}

4
common/mhfitem/doc.go Normal file
View File

@@ -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

View File

@@ -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)))

4
common/mhfmon/doc.go Normal file
View File

@@ -0,0 +1,4 @@
// Package mhfmon enumerates every monster in Monster Hunter Frontier by its
// internal enemy ID (em001em176) and provides metadata such as display name
// and large/small classification.
package mhfmon

View File

@@ -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},

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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) {

3
common/token/doc.go Normal file
View File

@@ -0,0 +1,3 @@
// Package token provides concurrency-safe random number generation and
// alphanumeric token generation for session tokens and warehouse IDs.
package token

View File

@@ -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