1 Commits

Author SHA1 Message Date
wish
4f889b1bf1 initial refactor-servers commit 2023-12-04 00:00:40 +11:00
606 changed files with 11973 additions and 11606 deletions

View File

@@ -1 +0,0 @@
bin/

View File

@@ -1,48 +0,0 @@
name: Create and publish a Docker image
# Configures this workflow to run every time a tag is created.
on:
push:
tags:
- '*'
# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds.
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.
jobs:
build-and-push-image:
runs-on: ubuntu-latest
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
permissions:
contents: read
packages: write
#
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
- name: Build and push Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

5
.gitignore vendored
View File

@@ -6,7 +6,4 @@ vendor/
savedata/*/
*.exe
*.lnk
*.bat
/docker/db-data
screenshots/*
/docker/Servers
*.bat

View File

@@ -1,30 +0,0 @@
# List of authors who contributed to Erupe
## Point of current development
The project is currently developed under https://github.com/ZeruLight/Erupe
## History of development
Development of this project dates back to 2019, and was developed under various umbrellas over time:
* Cappuccino (Fist/Ando/Ellie42) ("The Erupe Developers"), 2019-2020 (https://github.com/Ellie42/Erupe / https://github.com/ricochhet/Erupe-Legacy) (Still active closed source)
* Einherjar Team, ????-2022 Feb (There is no git history for this period, this team's work was taken and used as a foundation for future repositories)
* Community Edition, 2022 (https://github.com/xl3lackout/Erupe)
* sekaiwish Fork, 2022 (https://github.com/sekaiwish/Erupe)
* ZeruLight, 2022-2023 (https://github.com/ZeruLight/Erupe)
## Authorship of the code
Authorship is assigned for each commit within the git history, which is stored in these git repos:
* https://github.com/ZeruLight/Erupe
* https://github.com/Ellie42/Erupe
* https://github.com/ricochhet/Erupe-Legacy
* https://github.com/xl3lackout/Erupe
Note the divergence between Ellie42's branch and xl3lackout's where history has been lost.
Unfortunately, we have no detailed information on the history of Erupe before 2022.
If somebody can provide information, please contact us, so that we can make this history available.
## Exceptions with third-party libraries
The third-party libraries have their own way of addressing authorship and the authorship of commits importing/updating
a third-party library reflects who did the importing instead of who wrote the code within the commit.
The authors of third-party libraries are not explicitly mentioned, and usually is possible to obtain from the files belonging to the third-party libraries.

View File

@@ -1,14 +0,0 @@
FROM golang:1.21-alpine3.19
ENV GO111MODULE=on
WORKDIR /app/erupe
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
CMD [ "go", "run", "." ]

View File

@@ -1,6 +1,6 @@
# Erupe
## Client Compatibility
## Client Compatiblity
### Platforms
- PC
- PlayStation 3
@@ -32,20 +32,6 @@ If you want to modify or compile Erupe yourself, please read on.
3. Edit [config.json](./config.json) such that the database password matches your PostgreSQL setup.
4. Run `go build` or `go run .` to compile Erupe.
## Docker
Please see [docker/README.md](./docker/README.md). This is intended for quick installs and development, not for production.
## Schemas
We source control the following schemas:
- Initialization Schema: This initializes the application database to a specific version (9.1.0).
- Update Schemas: These are update files that should be ran on top of the initialization schema.
- Patch Schemas: These are for development and should be run after running all initialization and update schema. These get condensed into `Update Schemas` and deleted when updated to a new release.
- Bundled Schemas: These are demo reference files to give servers standard set-ups.
Note: Patch schemas are subject to change! You should only be using them if you are following along with development.
## Resources
- [Quest and Scenario Binary Files](https://files.catbox.moe/xf0l7w.7z)

2372
bin/quests/desktop.ini Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ package decryption
*/
import (
"erupe-ce/utils/byteframe"
"erupe-ce/common/byteframe"
"io"
)

View File

@@ -1,7 +1,6 @@
package mhfcourse
import (
"erupe-ce/config"
"math"
"sort"
"time"
@@ -67,15 +66,12 @@ func CourseExists(ID uint16, c []Course) bool {
// GetCourseStruct returns a slice of Course(s) from a rights integer
func GetCourseStruct(rights uint32) ([]Course, uint32) {
var resp []Course
for _, c := range config.GetConfig().DefaultCourses {
resp = append(resp, Course{ID: c})
}
resp := []Course{{ID: 1}, {ID: 23}, {ID: 24}}
s := Courses()
sort.Slice(s, func(i, j int) bool {
return s[i].ID > s[j].ID
})
var normalCafeCourseSet, netcafeCourseSet, hidenCourseSet bool
var normalCafeCourseSet, netcafeCourseSet bool
for _, course := range s {
if rights-course.Value() < 0x80000000 {
switch course.ID {
@@ -92,12 +88,6 @@ func GetCourseStruct(rights uint32) ([]Course, uint32) {
}
netcafeCourseSet = true
resp = append(resp, Course{ID: 30})
case 10:
if hidenCourseSet {
break
}
hidenCourseSet = true
resp = append(resp, Course{ID: 31})
}
course.Expiry = time.Date(2030, 1, 1, 0, 0, 0, 0, time.FixedZone("UTC+9", 9*60*60))
resp = append(resp, course)

View File

@@ -1,8 +1,7 @@
package pascalstring
import (
"erupe-ce/utils/byteframe"
"erupe-ce/common/byteframe"
"golang.org/x/text/encoding/japanese"
"golang.org/x/text/transform"
)

View File

@@ -6,7 +6,8 @@ import (
// StringStack is a basic LIFO "stack" for storing strings.
type StringStack struct {
stack []string
Locked bool
stack []string
}
// New creates a new instance of StringStack
@@ -19,6 +20,20 @@ func (s *StringStack) Set(v string) {
s.stack = []string{v}
}
// Lock freezes the StringStack
func (s *StringStack) Lock() {
if !s.Locked {
s.Locked = true
}
}
// Unlock unfreezes the StringStack
func (s *StringStack) Unlock() {
if s.Locked {
s.Locked = false
}
}
// Push pushes a string onto the stack.
func (s *StringStack) Push(v string) {
s.stack = append(s.stack, v)

View File

@@ -29,23 +29,6 @@ func SJISToUTF8(b []byte) string {
return string(result)
}
func ToNGWord(x string) []uint16 {
var w []uint16
for _, r := range []rune(x) {
if r > 0xFF {
t := UTF8ToSJIS(string(r))
if len(t) > 1 {
w = append(w, uint16(t[1])<<8|uint16(t[0]))
} else {
w = append(w, uint16(t[0]))
}
} else {
w = append(w, uint16(r))
}
}
return w
}
func PaddedString(x string, size uint, t bool) []byte {
if t {
e := japanese.ShiftJIS.NewEncoder()

View File

@@ -1,23 +1,30 @@
package token
import (
crand "crypto/rand"
"math/rand"
"time"
)
var RNG = NewRNG()
// Generate returns an alphanumeric token of specified length
func Generate(length int) string {
rng := RNG()
var chars = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
b := make([]rune, length)
for i := range b {
b[i] = chars[RNG.Intn(len(chars))]
b[i] = chars[rng.Intn(len(chars))]
}
return string(b)
}
// NewRNG returns a new NewRNG generator
func NewRNG() *rand.Rand {
// RNG returns a new RNG generator
func RNG() *rand.Rand {
return rand.New(rand.NewSource(time.Now().UnixNano()))
}
// RandBytes returns x random bytes
func RandBytes(x int) []byte {
y := make([]byte, x)
crand.Read(y)
return y
}

View File

@@ -9,52 +9,37 @@
],
"PatchServerManifest": "",
"PatchServerFile": "",
"Screenshots":{
"Enabled":true,
"Host":"127.0.0.1",
"Port":8080,
"OutputDir":"screenshots",
"UploadQuality":100
},
"ScreenshotAPIURL": "",
"DeleteOnSaveCorruption": false,
"ClientMode": "ZZ",
"QuestCacheExpiry": 300,
"ProxyPort": 0,
"CommandPrefix": "!",
"AutoCreateAccount": true,
"LoopDelay": 50,
"DefaultCourses": [1, 23, 24],
"EarthStatus": 0,
"EarthID": 0,
"EarthMonsters": [0, 0, 0, 0],
"SaveDumps": {
"Enabled": true,
"RawEnabled": false,
"OutputDir": "save-backups"
},
"DebugOptions": {
"DevMode": true,
"DevModeOptions": {
"AutoCreateAccount": true,
"CleanDB": false,
"MaxLauncherHR": false,
"LogInboundMessages": false,
"LogOutboundMessages": false,
"LogMessageData": false,
"MaxHexdumpLength": 256,
"DivaOverride": 0,
"FestaOverride": -1,
"TournamentOverride": 0,
"DivaEvent": 0,
"FestaEvent": -1,
"TournamentEvent": 0,
"DisableTokenCheck": false,
"QuestTools": false,
"AutoQuestBackport": true,
"ProxyPort": 0,
"CapLink": {
"Values": [51728, 20000, 51729, 1, 20000],
"Key": "",
"Host": "",
"Port": 80
"QuestDebugTools": false,
"EarthStatusOverride": 0,
"EarthIDOverride": 0,
"EarthMonsterOverride": [0, 0, 0, 0],
"SaveDumps": {
"Enabled": true,
"RawEnabled": false,
"OutputDir": "save-backups"
}
},
"GameplayOptions": {
"MinFeatureWeapons": 0,
"MaxFeatureWeapons": 1,
"FeaturedWeapons": 1,
"MaximumNP": 100000,
"MaximumRP": 50000,
"MaximumFP": 120000,
@@ -66,6 +51,8 @@
"ClanMemberLimits": [[0, 30], [3, 40], [7, 50], [10, 60]],
"BonusQuestAllowance": 3,
"DailyQuestAllowance": 1,
"MezfesSoloTickets": 10,
"MezfesGroupTickets": 4,
"LowLatencyRaviente": false,
"RegularRavienteMaxPlayers": 8,
"ViolentRavienteMaxPlayers": 8,
@@ -74,29 +61,12 @@
"SmallBerserkRavienteMaxPlayers": 8,
"GUrgentRate": 0.10,
"GCPMultiplier": 1.00,
"HRPMultiplier": 1.00,
"HRPMultiplierNC": 1.00,
"SRPMultiplier": 1.00,
"SRPMultiplierNC": 1.00,
"GRPMultiplier": 1.00,
"GRPMultiplierNC": 1.00,
"GSRPMultiplier": 1.00,
"GSRPMultiplierNC": 1.00,
"ZennyMultiplier": 1.00,
"ZennyMultiplierNC": 1.00,
"GZennyMultiplier": 1.00,
"GZennyMultiplierNC": 1.00,
"MaterialMultiplier": 1.00,
"MaterialMultiplierNC": 1.00,
"GMaterialMultiplier": 1.00,
"GMaterialMultiplierNC": 1.00,
"ExtraCarves": 0,
"ExtraCarvesNC": 0,
"GExtraCarves": 0,
"GExtraCarvesNC": 0,
"DisableHunterNavi": false,
"MezFesSoloTickets": 5,
"MezFesGroupTickets": 1,
"MezFesDuration": 172800,
"MezFesSwitchMinigame": false,
"EnableKaijiEvent": false,
@@ -108,58 +78,51 @@
"Discord": {
"Enabled": false,
"BotToken": "",
"RelayChannel": {
"Enabled": false,
"MaxMessageLength": 183,
"RelayChannelID": ""
}
"RealtimeChannelID": ""
},
"Commands": {
"help": {
"Commands": [
{
"Name": "Help",
"Enabled": true,
"Description": "Show enabled chat commands"
},
"rights":{
"Description": "Show enabled chat commands",
"Prefix": "help"
}, {
"Name": "Rights",
"Enabled": false,
"Description": "Overwrite the Rights value on your account"
},
"ravi": {
"Description": "Overwrite the Rights value on your account",
"Prefix": "rights"
}, {
"Name": "Raviente",
"Enabled": true,
"Description": "Various Raviente siege commands"
},
"teleport": {
"Description": "Various Raviente siege commands",
"Prefix": "ravi"
}, {
"Name": "Teleport",
"Enabled": false,
"Description": "Teleport to specified coordinates"
},
"reload": {
"Description": "Teleport to specified coordinates",
"Prefix": "tele"
}, {
"Name": "Reload",
"Enabled": true,
"Description": "Reload all players in your Land"
},
"kqf":{
"Description": "Reload all players in your Land",
"Prefix": "reload"
}, {
"Name": "KeyQuest",
"Enabled": false,
"Description": "Get or Set your HR Key Quest progress"
},
"course": {
"Description": "Overwrite your HR Key Quest progress",
"Prefix": "kqf"
}, {
"Name": "Course",
"Enabled": true,
"Description": "Toggle Courses on your account"
},
"psn": {
"Description": "Toggle Courses on your account",
"Prefix": "course"
}, {
"Name": "PSN",
"Enabled": true,
"Description": "Link a PlayStation Network ID to your account"
},
"discord": {
"Enabled": true,
"Description": "Generate a token to link your Discord account"
},
"ban": {
"Enabled": false,
"Description": "Ban/Temp Ban a user"
},
"timer": {
"Enabled": true,
"Description": "Toggle the Quest timer"
"Description": "Link a PlayStation Network ID to your account",
"Prefix": "psn"
}
},
],
"Courses": [
{"Name": "HunterLife", "Enabled": true},
{"Name": "Extra", "Enabled": true},
@@ -184,8 +147,8 @@
"Enabled": true,
"Port": 53312
},
"API": {
"Enabled": true,
"SignV2": {
"Enabled": false,
"Port": 8080,
"PatchServer": "",
"Banners": [],
@@ -193,45 +156,37 @@
"Links": []
},
"Channel": {
"Enabled": true
"Enabled": true,
"Worlds": [
{
"Name": "Novice", "Description": "Up to 2★", "IP": "", "Type": 3, "Recommended": 1, "AllowedClientFlags": 0,
"Lands": [{"MaxPlayers": 100}]
}, {
"Name": "Rookie", "Description": "Up to 4★", "IP": "", "Type": 3, "Recommended": 2, "AllowedClientFlags": 0,
"Lands": [{"MaxPlayers": 100}]
}, {
"Name": "Sincere", "Description": "All Quests", "IP": "", "Type": 1, "Recommended": 0, "AllowedClientFlags": 0,
"Lands": [{"MaxPlayers": 100}]
}, {
"Name": "Brave", "Description": "Only G Quests", "IP": "", "Type": 1, "Recommended": 5, "AllowedClientFlags": 0,
"Lands": [{"MaxPlayers": 100}]
}, {
"Name": "Noble", "Description": "All Quests", "IP": "", "Type": 2, "Recommended": 0, "AllowedClientFlags": 0,
"Lands": [{"MaxPlayers": 100}, {"MaxPlayers": 100}]
}, {
"Name": "Spirit", "Description": "All Quests", "IP": "", "Type": 4, "Recommended": 0, "AllowedClientFlags": 0,
"Lands": [{"MaxPlayers": 100}, {"MaxPlayers": 100}]
}, {
"Name": "Legend", "Description": "All Quests", "IP": "", "Type": 5, "Recommended": 0, "AllowedClientFlags": 0,
"Lands": [{"MaxPlayers": 100}]
}, {
"Name": "Fancy", "Description": "Minigames!", "IP": "", "Type": 6, "Recommended": 6, "AllowedClientFlags": 0,
"Lands": [{"MaxPlayers": 80}]
}
]
},
"Entrance": {
"Enabled": true,
"Port": 53310,
"Entries": [
{
"Name": "Newbie", "Description": "", "IP": "", "Type": 3, "Recommended": 2, "AllowedClientFlags": 0,
"Channels": [
{ "Port": 54001, "MaxPlayers": 100 },
{ "Port": 54002, "MaxPlayers": 100 }
]
}, {
"Name": "Normal", "Description": "", "IP": "", "Type": 1, "Recommended": 0, "AllowedClientFlags": 0,
"Channels": [
{ "Port": 54003, "MaxPlayers": 100 },
{ "Port": 54004, "MaxPlayers": 100 }
]
}, {
"Name": "Cities", "Description": "", "IP": "", "Type": 2, "Recommended": 0, "AllowedClientFlags": 0,
"Channels": [
{ "Port": 54005, "MaxPlayers": 100 }
]
}, {
"Name": "Tavern", "Description": "", "IP": "", "Type": 4, "Recommended": 0, "AllowedClientFlags": 0,
"Channels": [
{ "Port": 54006, "MaxPlayers": 100 }
]
}, {
"Name": "Return", "Description": "", "IP": "", "Type": 5, "Recommended": 0, "AllowedClientFlags": 0,
"Channels": [
{ "Port": 54007, "MaxPlayers": 100 }
]
}, {
"Name": "MezFes", "Description": "", "IP": "", "Type": 6, "Recommended": 6, "AllowedClientFlags": 0,
"Channels": [
{ "Port": 54008, "MaxPlayers": 100 }
]
}
]
"Port": 53310
}
}

View File

@@ -1,4 +1,4 @@
package config
package _config
import (
"fmt"
@@ -6,7 +6,6 @@ import (
"net"
"os"
"strings"
"sync"
"time"
"github.com/spf13/viper"
@@ -76,75 +75,56 @@ type Config struct {
LoginNotices []string // MHFML string of the login notices displayed
PatchServerManifest string // Manifest patch server override
PatchServerFile string // File patch server override
ScreenshotAPIURL string // Destination for screenshots uploaded to BBS
DeleteOnSaveCorruption bool // Attempts to save corrupted data will flag the save for deletion
ClientMode string
ClientID Mode
RealClientMode Mode
QuestCacheExpiry int // Number of seconds to keep quest data cached
ProxyPort uint16 // Forces the game to connect to a channel server proxy
CommandPrefix string // The prefix for commands
AutoCreateAccount bool // Automatically create accounts if they don't exist
LoopDelay int // Delay in milliseconds between each loop iteration
DefaultCourses []uint16
EarthStatus int32
EarthID int32
EarthMonsters []int32
SaveDumps SaveDumpOptions
Screenshots ScreenshotsOptions
DevMode bool
DebugOptions DebugOptions
DevModeOptions DevModeOptions
GameplayOptions GameplayOptions
Discord Discord
Commands map[string]Command
Commands []Command
Courses []Course
Database Database
Sign Sign
API API
SignV2 SignV2
Channel Channel
Entrance Entrance
}
// DevModeOptions holds various debug/temporary options for use while developing Erupe.
type DevModeOptions struct {
AutoCreateAccount bool // Automatically create accounts if they don't exist
CleanDB bool // Automatically wipes the DB on server reset.
MaxLauncherHR bool // Sets the HR returned in the launcher to HR7 so that you can join non-beginner worlds.
LogInboundMessages bool // Log all messages sent to the server
LogOutboundMessages bool // Log all messages sent to the clients
LogMessageData bool // Log all bytes transferred as a hexdump
MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled
DivaEvent int // Diva Defense event status
FestaEvent int // Hunter's Festa event status
TournamentEvent int // VS Tournament event status
DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!)
QuestDebugTools bool // Enable various quest debug logs
EarthStatusOverride int32
EarthIDOverride int32
EarthMonsterOverride []int32
SaveDumps SaveDumpOptions
}
type SaveDumpOptions struct {
Enabled bool
RawEnabled bool
OutputDir string
}
type ScreenshotsOptions struct {
Enabled bool
Host string // Destination for screenshots uploaded to BBS
Port uint32 // Port for screenshots API
OutputDir string
UploadQuality int //Determines the upload quality to the server
}
// DebugOptions holds various debug/temporary options for use while developing Erupe.
type DebugOptions struct {
CleanDB bool // Automatically wipes the DB on server reset.
MaxLauncherHR bool // Sets the HR returned in the launcher to HR7 so that you can join non-beginner worlds.
LogInboundMessages bool // Log all messages sent to the server
LogOutboundMessages bool // Log all messages sent to the clients
LogMessageData bool // Log all bytes transferred as a hexdump
MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled
DivaOverride int // Diva Defense event status
FestaOverride int // Hunter's Festa event status
TournamentOverride int // VS Tournament event status
DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!)
QuestTools bool // Enable various quest debug logs
AutoQuestBackport bool // Automatically backport quest files
ProxyPort uint16 // Forces the game to connect to a channel server proxy
CapLink CapLinkOptions
}
type CapLinkOptions struct {
Values []uint16
Key string
Host string
Port int
}
// GameplayOptions has various gameplay modifiers
type GameplayOptions struct {
MinFeatureWeapons int // Minimum number of Active Feature weapons to generate daily
MaxFeatureWeapons int // Maximum number of Active Feature weapons to generate daily
FeaturedWeapons int // Number of Active Feature weapons to generate daily
MaximumNP int // Maximum number of NP held by a player
MaximumRP uint16 // Maximum number of RP held by a player
MaximumFP uint32 // Maximum number of FP held by a player
@@ -157,6 +137,8 @@ type GameplayOptions struct {
ClanMemberLimits [][]uint8 // Array of maximum Clan Members -> [Rank, Members]
BonusQuestAllowance uint32 // Number of Bonus Point Quests to allow daily
DailyQuestAllowance uint32 // Number of Daily Quests to allow daily
MezfesSoloTickets uint32 // Number of solo tickets given weekly
MezfesGroupTickets uint32 // Number of group tickets given weekly
LowLatencyRaviente bool // Toggles low latency mode for Raviente, can be network intensive
RegularRavienteMaxPlayers uint8
ViolentRavienteMaxPlayers uint8
@@ -165,29 +147,12 @@ type GameplayOptions struct {
SmallBerserkRavienteMaxPlayers uint8
GUrgentRate float32 // Adjusts the rate of G Urgent quests spawning
GCPMultiplier float32 // Adjusts the multiplier of GCP rewarded for quest completion
HRPMultiplier float32 // Adjusts the multiplier of Hunter Rank Points rewarded for quest completion
HRPMultiplierNC float32 // Adjusts the multiplier of Hunter Rank Points rewarded for quest completion in a NetCafe
SRPMultiplier float32 // Adjusts the multiplier of Skill Rank Points rewarded for quest completion
SRPMultiplierNC float32 // Adjusts the multiplier of Skill Rank Points rewarded for quest completion in a NetCafe
GRPMultiplier float32 // Adjusts the multiplier of G Rank Points rewarded for quest completion
GRPMultiplierNC float32 // Adjusts the multiplier of G Rank Points rewarded for quest completion in a NetCafe
GSRPMultiplier float32 // Adjusts the multiplier of G Skill Rank Points rewarded for quest completion
GSRPMultiplierNC float32 // Adjusts the multiplier of G Skill Rank Points rewarded for quest completion in a NetCafe
ZennyMultiplier float32 // Adjusts the multiplier of Zenny rewarded for quest completion
ZennyMultiplierNC float32 // Adjusts the multiplier of Zenny rewarded for quest completion in a NetCafe
GZennyMultiplier float32 // Adjusts the multiplier of G Zenny rewarded for quest completion
GZennyMultiplierNC float32 // Adjusts the multiplier of G Zenny rewarded for quest completion in a NetCafe
MaterialMultiplier float32 // Adjusts the multiplier of Monster Materials rewarded for quest completion
MaterialMultiplierNC float32 // Adjusts the multiplier of Monster Materials rewarded for quest completion in a NetCafe
GMaterialMultiplier float32 // Adjusts the multiplier of G Rank Monster Materials rewarded for quest completion
GMaterialMultiplierNC float32 // Adjusts the multiplier of G Rank Monster Materials rewarded for quest completion in a NetCafe
ExtraCarves uint16 // Grant n extra chances to carve ALL carcasses
ExtraCarvesNC uint16 // Grant n extra chances to carve ALL carcasses in a NetCafe
GExtraCarves uint16 // Grant n extra chances to carve ALL G Rank carcasses
GExtraCarvesNC uint16 // Grant n extra chances to carve ALL G Rank carcasses in a NetCafe
DisableHunterNavi bool // Disables the Hunter Navi
MezFesSoloTickets uint32 // Number of solo tickets given weekly
MezFesGroupTickets uint32 // Number of group tickets given weekly
MezFesDuration int // Seconds that MezFes will last for weekly (from 12AM Mon backwards)
MezFesSwitchMinigame bool // Swaps out Volpakkun Together for Tokotoko Partnya
EnableKaijiEvent bool // Enables the Kaiji event in the Rasta Bar
@@ -199,15 +164,9 @@ type GameplayOptions struct {
// Discord holds the discord integration config.
type Discord struct {
Enabled bool
BotToken string
RelayChannel DiscordRelay
}
type DiscordRelay struct {
Enabled bool
MaxMessageLength int
RelayChannelID string
Enabled bool
BotToken string
RealtimeChannelID string
}
// Command is a channelserver chat command
@@ -239,29 +198,29 @@ type Sign struct {
Port int
}
// API holds server config
type API struct {
// SignV2 holds the new sign server config
type SignV2 struct {
Enabled bool
Port int
PatchServer string
Banners []APISignBanner
Messages []APISignMessage
Links []APISignLink
Banners []SignV2Banner
Messages []SignV2Message
Links []SignV2Link
}
type APISignBanner struct {
type SignV2Banner struct {
Src string `json:"src"` // Displayed image URL
Link string `json:"link"` // Link accessed on click
}
type APISignMessage struct {
type SignV2Message struct {
Message string `json:"message"` // Displayed message
Date int64 `json:"date"` // Displayed date
Kind int `json:"kind"` // 0 for 'Default', 1 for 'New'
Link string `json:"link"` // Link accessed on click
}
type APISignLink struct {
type SignV2Link struct {
Name string `json:"name"` // Displayed name
Icon string `json:"icon"` // Displayed icon. It will be cast as a monochrome color as long as it is transparent.
Link string `json:"link"` // Link accessed on click
@@ -269,42 +228,41 @@ type APISignLink struct {
type Channel struct {
Enabled bool
Worlds []World
}
type World struct {
IP string
Type uint8
Season uint8
Recommended uint8
Name string
Description string
AllowedClientFlags uint32
Lands []Land
}
type Land struct {
Port uint16
MaxPlayers uint16
}
// Entrance holds the entrance server config.
type Entrance struct {
Enabled bool
Port uint16
Entries []EntranceServerInfo
}
// EntranceServerInfo represents an entry in the serverlist.
type EntranceServerInfo struct {
IP string
Type uint8 // Server type. 0=?, 1=open, 2=cities, 3=newbie, 4=bar
Season uint8 // Server activity. 0 = green, 1 = orange, 2 = blue
Recommended uint8 // Something to do with server recommendation on 0, 3, and 5.
Name string // Server name, 66 byte null terminated Shift-JIS(JP) or Big5(TW).
Description string // Server description
// 4096(PC, PS3/PS4)?, 8258(PC, PS3/PS4)?, 8192 == nothing?
// THIS ONLY EXISTS IF Binary8Header.type == "SV2", NOT "SVR"!
AllowedClientFlags uint32
var ErupeConfig *Config
Channels []EntranceChannelInfo
func init() {
var err error
ErupeConfig, err = LoadConfig()
if err != nil {
preventClose(fmt.Sprintf("Failed to load config: %s", err.Error()))
}
}
// EntranceChannelInfo represents an entry in a server's channel list.
type EntranceChannelInfo struct {
Port uint16
MaxPlayers uint16
CurrentPlayers uint16
}
var (
once sync.Once
ErupeConfig *Config
)
// getOutboundIP4 gets the preferred outbound ip4 of this machine
// From https://stackoverflow.com/a/37382208
func getOutboundIP4() net.IP {
@@ -346,34 +304,21 @@ func LoadConfig() (*Config, error) {
for i := range versionStrings {
if strings.ToUpper(c.ClientMode) == versionStrings[i] {
c.ClientID = Mode(i + 1)
c.RealClientMode = Mode(i + 1)
c.ClientMode = strings.ToUpper(c.ClientMode)
if c.ClientID <= G101 {
if c.RealClientMode <= G101 {
c.ClientMode += " (Debug only)"
}
}
}
if c.ClientID == 0 {
if c.RealClientMode == 0 {
c.ClientMode = versionStrings[len(versionStrings)-1]
c.ClientID = ZZ
}
if c.GameplayOptions.MinFeatureWeapons > c.GameplayOptions.MaxFeatureWeapons {
c.GameplayOptions.MinFeatureWeapons = c.GameplayOptions.MaxFeatureWeapons
c.RealClientMode = ZZ
}
return c, nil
}
func GetConfig() *Config {
once.Do(func() {
var err error
ErupeConfig, err = LoadConfig()
if err != nil {
preventClose(fmt.Sprintf("Failed to load config: %s", err.Error()))
}
})
return ErupeConfig
}
func preventClose(text string) {
if ErupeConfig.DisableSoftCrash {
os.Exit(0)

View File

@@ -1,70 +0,0 @@
# Docker for erupe
## Building the container
Run the following from the route of the soruce folder. In this example we give it the tag of dev to seperate it from any other container verions.
```bash
docker build . -t erupe:dev
```
## Running the container in isolation
This is just running the container. You can do volume mounts into the container for the `config.json` to tell it to communicate to a database. You will need to do this also for other folders such as `bin` and `savedata`
```bash
docker run erupe:dev
```
## Docker compose
Docker compose allows you to run multiple containers at once. The docker compose in this folder has 3 things set up.
- postgres
- pg admin (Admin interface to make db changes)
- erupe
We automatically populate the database to the latest version on start. If you you are updating you will need to apply the new schemas manually.
Before we get started you should make sure the database info matches whats in the docker compose file for the environment variables `POSTGRES_PASSWORD`,`POSTGRES_USER` and `POSTGRES_DB`. You can set the host to be the service name `db`.
Here is a example of what you would put in the config.json if you was to leave the defaults. It is strongly recommended to change the password.
```txt
"Database": {
"Host": "db",
"Port": 5432,
"User": "postgres",
"Password": "password",
"Database": "erupe"
},
```
Place this file within ./docker/config.json
You will need to do the same for your bins place these in ./docker/bin
# Setting up the web hosted materials
Clone the Severs repo into ./docker/Severs
Make sure your hosts are pointing to where this is hosted
## Turning off the server safely
```bash
docker-compose stop
```
## Turning off the server destructive
```bash
docker-compose down
```
Make sure if you want to delete your data you delete the folders that persisted
- ./docker/savedata
- ./docker/db-data
## Turning on the server again
This boots the db pgadmin and the server in a detached state
```bash
docker-compose up -d
```
if you want all the logs and you want it to be in an attached state
```bash
docker-compose up
```
# Troubleshooting
Q: My Postgres will not populate. A: You're setup.sh is maybe saved as CRLF it needs to be saved as LF.

View File

@@ -1,71 +0,0 @@
version: "3.9"
# 1. docker-compose up db pgadmin
# 2. Use pgadmin to restore db and also apply patch-schema
# 3. Configure the config.json example. in docker you can point to the service name for the database i.e db
# 4. In seperate terminal docker-compose up server
# 5. If all went well happy hunting!
services:
db:
image: postgres
environment:
# (Make sure these match config.json)
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
- POSTGRES_DB=erupe
ports:
- "5432:5432"
volumes:
- ./db-data/:/var/lib/postgresql/data/
- ../schemas/:/schemas/
- ./init/setup.sh:/docker-entrypoint-initdb.d/setup.sh
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
pgadmin:
image: dpage/pgadmin4
restart: always
environment:
PGADMIN_DEFAULT_EMAIL: user@pgadmin.com
PGADMIN_DEFAULT_PASSWORD: password
ports:
- "5050:80"
depends_on:
db:
condition: service_healthy
server:
depends_on:
db:
condition: service_healthy
# If using prebuilt container change paths and config
build:
context: ../
volumes:
- ../config.json:/app/erupe/config.json
- ../bin:/app/erupe/bin
- ./savedata:/app/erupe/savedata
ports:
# (Make sure these match config.json)
- "53312:53312" #Sign V1
- "8080:8080" #Sign V2
- "53310:53310" #Entrance
# Channels
- "54001:54001"
- "54002:54002"
- "54003:54003"
- "54004:54004"
- "54005:54005"
- "54006:54006"
- "54007:54007"
- "54008:54008"
web:
image: httpd:latest
container_name: my-apache-app
ports:
- '80:80'
volumes:
- ./Servers:/usr/local/apache2/htdocs
depends_on:
db:
condition: service_healthy

View File

@@ -1,22 +0,0 @@
#!/bin/bash
set -e
echo "INIT!"
pg_restore --username="$POSTGRES_USER" --dbname="$POSTGRES_DB" --verbose /schemas/init.sql
echo "Updating!"
for file in /schemas/update-schema/*
do
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" -1 -f $file
done
echo "Patching!"
for file in /schemas/patch-schema/*
do
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" -1 -f $file
done

BIN
erupe.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

4
go.mod
View File

@@ -10,7 +10,7 @@ require (
github.com/lib/pq v1.10.9
github.com/spf13/viper v1.17.0
go.uber.org/zap v1.26.0
golang.org/x/crypto v0.17.0
golang.org/x/crypto v0.15.0
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
golang.org/x/text v0.14.0
)
@@ -32,7 +32,7 @@ require (
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/net v0.18.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/sys v0.14.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

8
go.sum
View File

@@ -220,8 +220,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -345,8 +345,8 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@@ -1,18 +0,0 @@
package constant
const (
BinaryMessageTypeState = 0
BinaryMessageTypeChat = 1
BinaryMessageTypeQuest = 2
BinaryMessageTypeData = 3
BinaryMessageTypeMailNotify = 4
BinaryMessageTypeEmote = 6
)
// MSG_SYS_CAST[ED]_BINARY broadcast types enum
const (
BroadcastTypeTargeted = 0x01
BroadcastTypeStage = 0x03
BroadcastTypeServer = 0x06
BroadcastTypeWorld = 0x0a
)

View File

@@ -1,15 +0,0 @@
package constant
import "erupe-ce/internal/model"
const (
FestivalColorNone model.FestivalColor = "none"
FestivalColorBlue model.FestivalColor = "blue"
FestivalColorRed model.FestivalColor = "red"
)
var FestivalColorCodes = map[model.FestivalColor]int16{
FestivalColorNone: -1,
FestivalColorBlue: 0,
FestivalColorRed: 1,
}

View File

@@ -1,8 +0,0 @@
package constant
import "erupe-ce/internal/model"
const (
GuildApplicationTypeApplied model.GuildApplicationType = "applied"
GuildApplicationTypeInvited model.GuildApplicationType = "invited"
)

View File

@@ -1,11 +0,0 @@
package model
type Achievement struct {
Level uint8
Value uint32
NextValue uint16
Required uint32
Updated bool
Progress uint32
Trophy uint8
}

View File

@@ -1,12 +0,0 @@
package model
type Airou struct {
ID uint32
Name []byte
Task uint8
Personality uint8
Class uint8
Experience uint32
WeaponType uint8
WeaponID uint16
}

View File

@@ -1,10 +0,0 @@
package model
type CafeBonus struct {
ID uint32 `db:"id"`
TimeReq uint32 `db:"time_req"`
ItemType uint32 `db:"item_type"`
ItemID uint32 `db:"item_id"`
Quantity uint32 `db:"quantity"`
Claimed bool `db:"claimed"`
}

View File

@@ -1,41 +0,0 @@
package model
import "time"
type CampaignEvent struct {
ID uint32
Unk0 uint32
MinHR int16
MaxHR int16
MinSR int16
MaxSR int16
MinGR int16
MaxGR int16
Unk1 uint16
Unk2 uint8
Unk3 uint8
Unk4 uint16
Unk5 uint16
Start time.Time
End time.Time
Unk6 uint8
String0 string
String1 string
String2 string
String3 string
Link string
Prefix string
Categories []uint16
}
type CampaignCategory struct {
ID uint16
Type uint8
Title string
Description string
}
type CampaignLink struct {
CategoryID uint16
CampaignID uint32
}

View File

@@ -1,27 +0,0 @@
package model
import "time"
type Distribution struct {
ID uint32 `db:"id"`
Deadline time.Time `db:"deadline"`
Rights uint32 `db:"rights"`
TimesAcceptable uint16 `db:"times_acceptable"`
TimesAccepted uint16 `db:"times_accepted"`
MinHR int16 `db:"min_hr"`
MaxHR int16 `db:"max_hr"`
MinSR int16 `db:"min_sr"`
MaxSR int16 `db:"max_sr"`
MinGR int16 `db:"min_gr"`
MaxGR int16 `db:"max_gr"`
EventName string `db:"event_name"`
Description string `db:"description"`
Selection bool `db:"selection"`
}
type DistributionItem struct {
ItemType uint8 `db:"item_type"`
ID uint32 `db:"id"`
ItemID uint32 `db:"item_id"`
Quantity uint32 `db:"quantity"`
}

View File

@@ -1,31 +0,0 @@
package model
import "time"
type Event struct {
EventType uint16
Unk1 uint16
Unk2 uint16
Unk3 uint16
Unk4 uint16
Unk5 uint32
Unk6 uint32
QuestFileIDs []uint16
}
type LoginBoost struct {
WeekReq uint8 `db:"week_req"`
WeekCount uint8
Active bool
Expiration time.Time `db:"expiration"`
Reset time.Time `db:"reset"`
}
type ActiveFeature struct {
StartTime time.Time `db:"start_time"`
ActiveFeatures uint32 `db:"featured"`
}
type TrendWeapon struct {
WeaponType uint8
WeaponID uint16
}

View File

@@ -1,32 +0,0 @@
package model
type FestaTrial struct {
ID uint32 `db:"id"`
Objective uint16 `db:"objective"`
GoalID uint32 `db:"goal_id"`
TimesReq uint16 `db:"times_req"`
Locale uint16 `db:"locale_req"`
Reward uint16 `db:"reward"`
Monopoly FestivalColor `db:"monopoly"`
Unk uint16
}
type FestaReward struct {
Unk0 uint8
Unk1 uint8
ItemType uint16
Quantity uint16
ItemID uint16
Unk5 uint16
Unk6 uint16
Unk7 uint8
}
type FestaPrize struct {
ID uint32 `db:"id"`
Tier uint32 `db:"tier"`
SoulsReq uint32 `db:"souls_req"`
ItemID uint32 `db:"item_id"`
NumItem uint32 `db:"num_item"`
Claimed int `db:"claimed"`
}

View File

@@ -1,58 +0,0 @@
package model
type ShopItem struct {
ID uint32 `db:"id"`
ItemID uint32 `db:"item_id"`
Cost uint32 `db:"cost"`
Quantity uint16 `db:"quantity"`
MinHR uint16 `db:"min_hr"`
MinSR uint16 `db:"min_sr"`
MinGR uint16 `db:"min_gr"`
StoreLevel uint8 `db:"store_level"`
MaxQuantity uint16 `db:"max_quantity"`
UsedQuantity uint16 `db:"used_quantity"`
RoadFloors uint16 `db:"road_floors"`
RoadFatalis uint16 `db:"road_fatalis"`
}
type Gacha struct {
ID uint32 `db:"id"`
MinGR uint32 `db:"min_gr"`
MinHR uint32 `db:"min_hr"`
Name string `db:"name"`
URLBanner string `db:"url_banner"`
URLFeature string `db:"url_feature"`
URLThumbnail string `db:"url_thumbnail"`
Wide bool `db:"wide"`
Recommended bool `db:"recommended"`
GachaType uint8 `db:"gacha_type"`
Hidden bool `db:"hidden"`
}
type GachaEntry struct {
EntryType uint8 `db:"entry_type"`
ID uint32 `db:"id"`
ItemType uint8 `db:"item_type"`
ItemNumber uint32 `db:"item_number"`
ItemQuantity uint16 `db:"item_quantity"`
Weight float64 `db:"weight"`
Rarity uint8 `db:"rarity"`
Rolls uint8 `db:"rolls"`
FrontierPoints uint16 `db:"frontier_points"`
DailyLimit uint8 `db:"daily_limit"`
Name string `db:"name"`
}
type GachaItem struct {
ItemType uint8 `db:"item_type"`
ItemID uint16 `db:"item_id"`
Quantity uint16 `db:"quantity"`
}
type FPointExchange struct {
ID uint32 `db:"id"`
ItemType uint8 `db:"item_type"`
ItemID uint16 `db:"item_id"`
Quantity uint16 `db:"quantity"`
FPoints uint16 `db:"fpoints"`
Buyable bool `db:"buyable"`
}

View File

@@ -1,102 +0,0 @@
package model
import "time"
type GuildAdventure struct {
ID uint32 `db:"id"`
Destination uint32 `db:"destination"`
Charge uint32 `db:"charge"`
Depart uint32 `db:"depart"`
Return uint32 `db:"return"`
CollectedBy string `db:"collected_by"`
}
type GuildTreasureHunt struct {
HuntID uint32 `db:"id"`
HostID uint32 `db:"host_id"`
Destination uint32 `db:"destination"`
Level uint32 `db:"level"`
Start time.Time `db:"start"`
Acquired bool `db:"acquired"`
Collected bool `db:"collected"`
HuntData []byte `db:"hunt_data"`
Hunters uint32 `db:"hunters"`
Claimed bool `db:"claimed"`
}
type GuildTreasureSouvenir struct {
Destination uint32
Quantity uint32
}
type FestivalColor string
type GuildApplicationType string
type GuildIconPart struct {
Index uint16
ID uint16
Page uint8
Size uint8
Rotation uint8
Red uint8
Green uint8
Blue uint8
PosX uint16
PosY uint16
}
type GuildApplication struct {
ID int `db:"id"`
GuildID uint32 `db:"guild_id"`
CharID uint32 `db:"character_id"`
ActorID uint32 `db:"actor_id"`
ApplicationType GuildApplicationType `db:"application_type"`
CreatedAt time.Time `db:"created_at"`
}
type GuildLeader struct {
LeaderCharID uint32 `db:"leader_id"`
LeaderName string `db:"leader_name"`
}
type MessageBoardPost struct {
ID uint32 `db:"id"`
StampID uint32 `db:"stamp_id"`
Title string `db:"title"`
Body string `db:"body"`
AuthorID uint32 `db:"author_id"`
Timestamp time.Time `db:"created_at"`
LikedBy string `db:"liked_by"`
}
type GuildMeal struct {
ID uint32 `db:"id"`
MealID uint32 `db:"meal_id"`
Level uint32 `db:"level"`
CreatedAt time.Time `db:"created_at"`
}
type GuildMission struct {
ID uint32
Unk uint32
Type uint16
Goal uint16
Quantity uint16
SkipTickets uint16
GR bool
RewardType uint16
RewardLevel uint16
}
type GuildAllianceInvite struct {
GuildID uint32
LeaderID uint32
Unk0 uint16
Unk1 uint16
Members uint16
GuildName string
LeaderName string
}
type UnkGuildInfo struct {
Unk0 uint8
Unk1 uint8
Unk2 uint8
}

View File

@@ -1,17 +0,0 @@
package model
import "time"
type HouseData struct {
CharID uint32 `db:"id"`
HR uint16 `db:"hr"`
GR uint16 `db:"gr"`
Name string `db:"name"`
HouseState uint8 `db:"house_state"`
HousePassword string `db:"house_password"`
}
type Title struct {
ID uint16 `db:"id"`
Acquired time.Time `db:"unlocked_at"`
Updated time.Time `db:"updated_at"`
}

View File

@@ -1,40 +0,0 @@
package model
import "time"
type PaperMissionTimetable struct {
Start time.Time
End time.Time
}
type PaperMissionData struct {
Unk0 uint8
Unk1 uint8
Unk2 int16
Reward1ID uint16
Reward1Quantity uint8
Reward2ID uint16
Reward2Quantity uint8
}
type PaperMission struct {
Timetables []PaperMissionTimetable
Data []PaperMissionData
}
type PaperData struct {
Unk0 uint16
Unk1 int16
Unk2 int16
Unk3 int16
Unk4 int16
Unk5 int16
Unk6 int16
}
type PaperGift struct {
Unk0 uint16
Unk1 uint8
Unk2 uint8
Unk3 uint16
}

View File

@@ -1,36 +0,0 @@
package model
import "time"
type RyoudamaReward struct {
Unk0 uint8
Unk1 uint8
Unk2 uint16
Unk3 uint16
Unk4 uint16
Unk5 uint16
}
type RyoudamaKeyScore struct {
Unk0 uint8
Unk1 int32
}
type RyoudamaCharInfo struct {
CID uint32
Unk0 int32
Name string
}
type RyoudamaBoostInfo struct {
Start time.Time
End time.Time
}
type Ryoudama struct {
Reward []RyoudamaReward
KeyScore []RyoudamaKeyScore
CharInfo []RyoudamaCharInfo
BoostInfo []RyoudamaBoostInfo
Score []int32
}

View File

@@ -1,53 +0,0 @@
package model
import "time"
type SeibattleTimetable struct {
Start time.Time
End time.Time
}
type SeibattleKeyScore struct {
Unk0 uint8
Unk1 int32
}
type SeibattleCareer struct {
Unk0 uint16
Unk1 uint16
Unk2 uint16
}
type SeibattleOpponent struct {
Unk0 int32
Unk1 int8
}
type SeibattleConventionResult struct {
Unk0 uint32
Unk1 uint16
Unk2 uint16
Unk3 uint16
Unk4 uint16
}
type SeibattleCharScore struct {
Unk0 uint32
}
type SeibattleCurResult struct {
Unk0 uint32
Unk1 uint16
Unk2 uint16
Unk3 uint16
}
type Seibattle struct {
Timetable []SeibattleTimetable
KeyScore []SeibattleKeyScore
Career []SeibattleCareer
Opponent []SeibattleOpponent
ConventionResult []SeibattleConventionResult
CharScore []SeibattleCharScore
CurResult []SeibattleCurResult
}

View File

@@ -1,45 +0,0 @@
package model
import "time"
type TournamentInfo0 struct {
ID uint32
MaxPlayers uint32
CurrentPlayers uint32
Unk1 uint16
TextColor uint16
Unk2 uint32
Time1 time.Time
Time2 time.Time
Time3 time.Time
Time4 time.Time
Time5 time.Time
Time6 time.Time
Unk3 uint8
Unk4 uint8
MinHR uint32
MaxHR uint32
Unk5 string
Unk6 string
}
type TournamentInfo21 struct {
Unk0 uint32
Unk1 uint32
Unk2 uint32
Unk3 uint8
}
type TournamentInfo22 struct {
Unk0 uint32
Unk1 uint32
Unk2 uint32
Unk3 uint8
Unk4 string
}
type TournamentReward struct {
Unk0 uint16
Unk1 uint16
Unk2 uint16
}

View File

@@ -1,99 +0,0 @@
package model
import "time"
type TowerInfoTRP struct {
TR int32
TRP int32
}
type TowerInfoSkill struct {
TSP int32
Skills []int16 // 64
}
type TowerInfoHistory struct {
Unk0 []int16 // 5
Unk1 []int16 // 5
}
type TowerInfoLevel struct {
Floors int32
Unk1 int32
Unk2 int32
Unk3 int32
}
type GemInfo struct {
Gem uint16
Quantity uint16
}
type GemHistory struct {
Gem uint16
Message uint16
Timestamp time.Time
Sender string
}
type TenrouiraiProgress struct {
Page uint8
Mission1 uint16
Mission2 uint16
Mission3 uint16
}
type TenrouiraiReward struct {
Index uint8
Item []uint16 // 5
Quantity []uint8 // 5
}
type TenrouiraiKeyScore struct {
Unk0 uint8
Unk1 int32
}
type TenrouiraiData struct {
Block uint8
Mission uint8
// 1 = Floors climbed
// 2 = Collect antiques
// 3 = Open chests
// 4 = Cats saved
// 5 = TRP acquisition
// 6 = Monster slays
Goal uint16
Cost uint16
Skill1 uint8 // 80
Skill2 uint8 // 40
Skill3 uint8 // 40
Skill4 uint8 // 20
Skill5 uint8 // 40
Skill6 uint8 // 50
}
type TenrouiraiCharScore struct {
Score int32
Name string
}
type TenrouiraiTicket struct {
Unk0 uint8
RP uint32
Unk2 uint32
}
type Tenrouirai struct {
Progress []TenrouiraiProgress
Reward []TenrouiraiReward
KeyScore []TenrouiraiKeyScore
Data []TenrouiraiData
CharScore []TenrouiraiCharScore
Ticket []TenrouiraiTicket
}
type TowerInfo struct {
TRP []TowerInfoTRP
Skill []TowerInfoSkill
History []TowerInfoHistory
Level []TowerInfoLevel
}

View File

@@ -1,641 +0,0 @@
package service
import (
"database/sql"
"database/sql/driver"
"encoding/json"
"errors"
"erupe-ce/config"
"erupe-ce/internal/model"
"erupe-ce/utils/database"
"erupe-ce/utils/logger"
"fmt"
"time"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
)
type Guild struct {
ID uint32 `db:"id"`
Name string `db:"name"`
MainMotto uint8 `db:"main_motto"`
SubMotto uint8 `db:"sub_motto"`
CreatedAt time.Time `db:"created_at"`
MemberCount uint16 `db:"member_count"`
RankRP uint32 `db:"rank_rp"`
EventRP uint32 `db:"event_rp"`
RoomRP uint16 `db:"room_rp"`
RoomExpiry time.Time `db:"room_expiry"`
Comment string `db:"comment"`
PugiName1 string `db:"pugi_name_1"`
PugiName2 string `db:"pugi_name_2"`
PugiName3 string `db:"pugi_name_3"`
PugiOutfit1 uint8 `db:"pugi_outfit_1"`
PugiOutfit2 uint8 `db:"pugi_outfit_2"`
PugiOutfit3 uint8 `db:"pugi_outfit_3"`
PugiOutfits uint32 `db:"pugi_outfits"`
Recruiting bool `db:"recruiting"`
FestivalColor model.FestivalColor `db:"festival_color"`
Souls uint32 `db:"souls"`
AllianceID uint32 `db:"alliance_id"`
Icon *GuildIcon `db:"icon"`
model.GuildLeader
}
func RollbackTransaction(transaction *sql.Tx) {
err := transaction.Rollback()
logger := logger.Get()
if err != nil {
logger.Error("failed to rollback transaction", zap.Error(err))
}
}
type GuildIcon struct {
Parts []model.GuildIconPart
}
func (gi *GuildIcon) Scan(val interface{}) (err error) {
switch v := val.(type) {
case []byte:
err = json.Unmarshal(v, &gi)
case string:
err = json.Unmarshal([]byte(v), &gi)
}
return
}
func (gi *GuildIcon) Value() (valuer driver.Value, err error) {
return json.Marshal(gi)
}
func (g *Guild) Rank() uint16 {
rpMap := []uint32{
24, 48, 96, 144, 192, 240, 288, 360, 432,
504, 600, 696, 792, 888, 984, 1080, 1200,
}
if config.GetConfig().ClientID <= config.Z2 {
rpMap = []uint32{
3500, 6000, 8500, 11000, 13500, 16000, 20000, 24000, 28000,
33000, 38000, 43000, 48000, 55000, 70000, 90000, 120000,
}
}
for i, u := range rpMap {
if g.RankRP < u {
if config.GetConfig().ClientID <= config.S6 && i >= 12 {
return 12
} else if config.GetConfig().ClientID <= config.F5 && i >= 13 {
return 13
} else if config.GetConfig().ClientID <= config.G32 && i >= 14 {
return 14
}
return uint16(i)
}
}
if config.GetConfig().ClientID <= config.S6 {
return 12
} else if config.GetConfig().ClientID <= config.F5 {
return 13
} else if config.GetConfig().ClientID <= config.G32 {
return 14
}
return 17
}
func (guild *Guild) Save() error {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
_, err = db.Exec(`
UPDATE guilds SET main_motto=$2, sub_motto=$3, comment=$4, pugi_name_1=$5, pugi_name_2=$6, pugi_name_3=$7,
pugi_outfit_1=$8, pugi_outfit_2=$9, pugi_outfit_3=$10, pugi_outfits=$11, icon=$12, leader_id=$13 WHERE id=$1
`, guild.ID, guild.MainMotto, guild.SubMotto, guild.Comment, guild.PugiName1, guild.PugiName2, guild.PugiName3,
guild.PugiOutfit1, guild.PugiOutfit2, guild.PugiOutfit3, guild.PugiOutfits, guild.Icon, guild.GuildLeader.LeaderCharID)
if err != nil {
logger.Error("failed to update guild data", zap.Error(err), zap.Uint32("guildID", guild.ID))
return err
}
return nil
}
func (guild *Guild) CreateApplication(charID uint32, applicationType model.GuildApplicationType, transaction *sql.Tx, actorId uint32) error {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
query := `
INSERT INTO guild_applications (guild_id, character_id, actor_id, application_type)
VALUES ($1, $2, $3, $4)
`
// Actor ID is the s.CharID
if transaction == nil {
_, err = db.Exec(query, guild.ID, charID, actorId, applicationType)
} else {
_, err = transaction.Exec(query, guild.ID, charID, actorId, applicationType)
}
if err != nil {
logger.Error(
"failed to add guild application",
zap.Error(err),
zap.Uint32("guildID", guild.ID),
zap.Uint32("charID", charID),
)
return err
}
return nil
}
func (guild *Guild) Disband(charID uint32) error {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
transaction, err := db.Begin()
if err != nil {
logger.Error("failed to begin transaction", zap.Error(err))
return err
}
_, err = transaction.Exec("DELETE FROM guild_characters WHERE guild_id = $1", guild.ID)
if err != nil {
logger.Error("failed to remove guild characters", zap.Error(err), zap.Uint32("guildId", guild.ID))
RollbackTransaction(transaction)
return err
}
_, err = transaction.Exec("DELETE FROM guilds WHERE id = $1", guild.ID)
if err != nil {
logger.Error("failed to remove guild", zap.Error(err), zap.Uint32("guildID", guild.ID))
RollbackTransaction(transaction)
return err
}
_, err = transaction.Exec("DELETE FROM guild_alliances WHERE parent_id=$1", guild.ID)
if err != nil {
logger.Error("failed to remove guild alliance", zap.Error(err), zap.Uint32("guildID", guild.ID))
RollbackTransaction(transaction)
return err
}
_, err = transaction.Exec("UPDATE guild_alliances SET sub1_id=sub2_id, sub2_id=NULL WHERE sub1_id=$1", guild.ID)
if err != nil {
logger.Error("failed to remove guild from alliance", zap.Error(err), zap.Uint32("guildID", guild.ID))
RollbackTransaction(transaction)
return err
}
_, err = transaction.Exec("UPDATE guild_alliances SET sub2_id=NULL WHERE sub2_id=$1", guild.ID)
if err != nil {
logger.Error("failed to remove guild from alliance", zap.Error(err), zap.Uint32("guildID", guild.ID))
RollbackTransaction(transaction)
return err
}
err = transaction.Commit()
if err != nil {
logger.Error("failed to commit transaction", zap.Error(err))
return err
}
logger.Info("Character disbanded guild", zap.Uint32("charID", charID), zap.Uint32("guildID", guild.ID))
return nil
}
func (guild *Guild) RemoveCharacter(charID uint32) error {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
_, err = db.Exec("DELETE FROM guild_characters WHERE character_id=$1", charID)
if err != nil {
logger.Error(
"failed to remove character from guild",
zap.Error(err),
zap.Uint32("charID", charID),
zap.Uint32("guildID", guild.ID),
)
return err
}
return nil
}
func (guild *Guild) AcceptApplication(charID uint32) error {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
transaction, err := db.Begin()
if err != nil {
logger.Error("failed to start db transaction", zap.Error(err))
return err
}
_, err = transaction.Exec(`DELETE FROM guild_applications WHERE character_id = $1`, charID)
if err != nil {
logger.Error("failed to accept character's guild application", zap.Error(err))
RollbackTransaction(transaction)
return err
}
_, err = transaction.Exec(`
INSERT INTO guild_characters (guild_id, character_id, order_index)
VALUES ($1, $2, (SELECT MAX(order_index) + 1 FROM guild_characters WHERE guild_id = $1))
`, guild.ID, charID)
if err != nil {
logger.Error(
"failed to add applicant to guild",
zap.Error(err),
zap.Uint32("guildID", guild.ID),
zap.Uint32("charID", charID),
)
RollbackTransaction(transaction)
return err
}
err = transaction.Commit()
if err != nil {
logger.Error("failed to commit db transaction", zap.Error(err))
RollbackTransaction(transaction)
return err
}
return nil
}
// This is relying on the fact that invitation ID is also character ID right now
// if invitation ID changes, this will break.
func (guild *Guild) CancelInvitation(charID uint32) error {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
_, err = db.Exec(
`DELETE FROM guild_applications WHERE character_id = $1 AND guild_id = $2 AND application_type = 'invited'`,
charID, guild.ID,
)
if err != nil {
logger.Error(
"failed to cancel guild invitation",
zap.Error(err),
zap.Uint32("guildID", guild.ID),
zap.Uint32("charID", charID),
)
return err
}
return nil
}
func (guild *Guild) RejectApplication(charID uint32) error {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
_, err = db.Exec(
`DELETE FROM guild_applications WHERE character_id = $1 AND guild_id = $2 AND application_type = 'applied'`,
charID, guild.ID,
)
if err != nil {
logger.Error(
"failed to reject guild application",
zap.Error(err),
zap.Uint32("guildID", guild.ID),
zap.Uint32("charID", charID),
)
return err
}
return nil
}
func (guild *Guild) ArrangeCharacters(charIDs []uint32) error {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
transaction, err := db.Begin()
if err != nil {
logger.Error("failed to start db transaction", zap.Error(err))
return err
}
for i, id := range charIDs {
_, err := transaction.Exec("UPDATE guild_characters SET order_index = $1 WHERE character_id = $2", 2+i, id)
if err != nil {
err = transaction.Rollback()
if err != nil {
logger.Error("failed to rollback db transaction", zap.Error(err))
}
return err
}
}
err = transaction.Commit()
if err != nil {
logger.Error("failed to commit db transaction", zap.Error(err))
return err
}
return nil
}
func (guild *Guild) GetApplicationForCharID(charID uint32, applicationType model.GuildApplicationType) (*model.GuildApplication, error) {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
row := db.QueryRowx(`
SELECT * from guild_applications WHERE character_id = $1 AND guild_id = $2 AND application_type = $3
`, charID, guild.ID, applicationType)
application := &model.GuildApplication{}
err = row.StructScan(application)
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
if err != nil {
logger.Error(
"failed to retrieve guild application for character",
zap.Error(err),
zap.Uint32("charID", charID),
zap.Uint32("guildID", guild.ID),
)
return nil, err
}
return application, nil
}
func (guild *Guild) HasApplicationForCharID(charID uint32) (bool, error) {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
row := db.QueryRowx(`
SELECT 1 from guild_applications WHERE character_id = $1 AND guild_id = $2
`, charID, guild.ID)
num := 0
err = row.Scan(&num)
if errors.Is(err, sql.ErrNoRows) {
return false, nil
}
if err != nil {
logger.Error(
"failed to retrieve guild applications for character",
zap.Error(err),
zap.Uint32("charID", charID),
zap.Uint32("guildID", guild.ID),
)
return false, err
}
return true, nil
}
const GuildInfoSelectQuery = `
SELECT
g.id,
g.name,
rank_rp,
event_rp,
room_rp,
COALESCE(room_expiry, '1970-01-01') AS room_expiry,
main_motto,
sub_motto,
created_at,
leader_id,
c.name AS leader_name,
comment,
COALESCE(pugi_name_1, '') AS pugi_name_1,
COALESCE(pugi_name_2, '') AS pugi_name_2,
COALESCE(pugi_name_3, '') AS pugi_name_3,
pugi_outfit_1,
pugi_outfit_2,
pugi_outfit_3,
pugi_outfits,
recruiting,
COALESCE((SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id), 'none') AS festival_color,
COALESCE((SELECT SUM(fs.souls) FROM festa_submissions fs WHERE fs.guild_id=g.id), 0) AS souls,
COALESCE((
SELECT id FROM guild_alliances ga WHERE
ga.parent_id = g.id OR
ga.sub1_id = g.id OR
ga.sub2_id = g.id
), 0) AS alliance_id,
icon,
(SELECT count(1) FROM guild_characters gc WHERE gc.guild_id = g.id) AS member_count
FROM guilds g
JOIN guild_characters gc ON gc.character_id = leader_id
JOIN characters c on leader_id = c.id
`
func CreateGuild(guildName string, charID uint32) (int32, error) {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
transaction, err := db.Begin()
if err != nil {
logger.Error("failed to start db transaction", zap.Error(err))
return 0, err
}
if err != nil {
panic(err)
}
guildResult, err := transaction.Query(
"INSERT INTO guilds (name, leader_id) VALUES ($1, $2) RETURNING id",
guildName, charID,
)
if err != nil {
logger.Error("failed to create guild", zap.Error(err))
RollbackTransaction(transaction)
return 0, err
}
var guildId int32
guildResult.Next()
err = guildResult.Scan(&guildId)
if err != nil {
logger.Error("failed to retrieve guild ID", zap.Error(err))
RollbackTransaction(transaction)
return 0, err
}
err = guildResult.Close()
if err != nil {
logger.Error("failed to finalise query", zap.Error(err))
RollbackTransaction(transaction)
return 0, err
}
_, err = transaction.Exec(`
INSERT INTO guild_characters (guild_id, character_id)
VALUES ($1, $2)
`, guildId, charID)
if err != nil {
logger.Error("failed to add character to guild", zap.Error(err))
RollbackTransaction(transaction)
return 0, err
}
err = transaction.Commit()
if err != nil {
logger.Error("failed to commit guild creation", zap.Error(err))
return 0, err
}
return guildId, nil
}
func GetGuildInfoByID(guildID uint32) (*Guild, error) {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := db.Queryx(fmt.Sprintf(`
%s
WHERE g.id = $1
LIMIT 1
`, GuildInfoSelectQuery), guildID)
if err != nil {
logger.Error("failed to retrieve guild", zap.Error(err), zap.Uint32("guildID", guildID))
return nil, err
}
defer rows.Close()
hasRow := rows.Next()
if !hasRow {
return nil, nil
}
return BuildGuildObjectFromDbResult(rows, err)
}
func GetGuildInfoByCharacterId(charID uint32) (*Guild, error) {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := db.Queryx(fmt.Sprintf(`
%s
WHERE EXISTS(
SELECT 1
FROM guild_characters gc1
WHERE gc1.character_id = $1
AND gc1.guild_id = g.id
)
OR EXISTS(
SELECT 1
FROM guild_applications ga
WHERE ga.character_id = $1
AND ga.guild_id = g.id
AND ga.application_type = 'applied'
)
LIMIT 1
`, GuildInfoSelectQuery), charID)
if err != nil {
logger.Error("failed to retrieve guild for character", zap.Error(err), zap.Uint32("charID", charID))
return nil, err
}
defer rows.Close()
hasRow := rows.Next()
if !hasRow {
return nil, nil
}
return BuildGuildObjectFromDbResult(rows, err)
}
func BuildGuildObjectFromDbResult(result *sqlx.Rows, err error) (*Guild, error) {
guild := &Guild{}
logger := logger.Get()
err = result.StructScan(guild)
if err != nil {
logger.Error("failed to retrieve guild data from database", zap.Error(err))
return nil, err
}
return guild, nil
}

View File

@@ -1,112 +0,0 @@
package service
import (
"erupe-ce/utils/database"
"erupe-ce/utils/logger"
"fmt"
"time"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
)
type GuildAlliance struct {
ID uint32 `db:"id"`
Name string `db:"name"`
CreatedAt time.Time `db:"created_at"`
TotalMembers uint16
ParentGuildID uint32 `db:"parent_id"`
SubGuild1ID uint32 `db:"sub1_id"`
SubGuild2ID uint32 `db:"sub2_id"`
ParentGuild Guild
SubGuild1 Guild
SubGuild2 Guild
}
const AllianceInfoSelectQuery = `
SELECT
ga.id,
ga.name,
created_at,
parent_id,
CASE
WHEN sub1_id IS NULL THEN 0
ELSE sub1_id
END,
CASE
WHEN sub2_id IS NULL THEN 0
ELSE sub2_id
END
FROM guild_alliances ga
`
func GetAllianceData(AllianceID uint32) (*GuildAlliance, error) {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := db.Queryx(fmt.Sprintf(`
%s
WHERE ga.id = $1
`, AllianceInfoSelectQuery), AllianceID)
if err != nil {
logger.Error("Failed to retrieve alliance data from database", zap.Error(err))
return nil, err
}
defer rows.Close()
hasRow := rows.Next()
if !hasRow {
return nil, nil
}
return BuildAllianceObjectFromDbResult(rows, err)
}
func BuildAllianceObjectFromDbResult(result *sqlx.Rows, err error) (*GuildAlliance, error) {
alliance := &GuildAlliance{}
logger := logger.Get()
err = result.StructScan(alliance)
if err != nil {
logger.Error("failed to retrieve alliance from database", zap.Error(err))
return nil, err
}
parentGuild, err := GetGuildInfoByID(alliance.ParentGuildID)
if err != nil {
logger.Error("Failed to get parent guild info", zap.Error(err))
return nil, err
} else {
alliance.ParentGuild = *parentGuild
alliance.TotalMembers += parentGuild.MemberCount
}
if alliance.SubGuild1ID > 0 {
subGuild1, err := GetGuildInfoByID(alliance.SubGuild1ID)
if err != nil {
logger.Error("Failed to get sub guild 1 info", zap.Error(err))
return nil, err
} else {
alliance.SubGuild1 = *subGuild1
alliance.TotalMembers += subGuild1.MemberCount
}
}
if alliance.SubGuild2ID > 0 {
subGuild2, err := GetGuildInfoByID(alliance.SubGuild2ID)
if err != nil {
logger.Error("Failed to get sub guild 2 info", zap.Error(err))
return nil, err
} else {
alliance.SubGuild2 = *subGuild2
alliance.TotalMembers += subGuild2.MemberCount
}
}
return alliance, nil
}

View File

@@ -1,169 +0,0 @@
package service
import (
"erupe-ce/utils/database"
"erupe-ce/utils/logger"
"fmt"
"time"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
)
type GuildMember struct {
GuildID uint32 `db:"guild_id"`
CharID uint32 `db:"character_id"`
JoinedAt *time.Time `db:"joined_at"`
Souls uint32 `db:"souls"`
RPToday uint16 `db:"rp_today"`
RPYesterday uint16 `db:"rp_yesterday"`
Name string `db:"name"`
IsApplicant bool `db:"is_applicant"`
OrderIndex uint16 `db:"order_index"`
LastLogin uint32 `db:"last_login"`
Recruiter bool `db:"recruiter"`
AvoidLeadership bool `db:"avoid_leadership"`
IsLeader bool `db:"is_leader"`
HR uint16 `db:"hr"`
GR uint16 `db:"gr"`
WeaponID uint16 `db:"weapon_id"`
WeaponType uint8 `db:"weapon_type"`
}
func (gm *GuildMember) CanRecruit() bool {
if gm.Recruiter {
return true
}
if gm.OrderIndex <= 3 {
return true
}
if gm.IsLeader {
return true
}
return false
}
func (gm *GuildMember) IsSubLeader() bool {
return gm.OrderIndex <= 3
}
func (gm *GuildMember) Save() error {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
_, err = db.Exec("UPDATE guild_characters SET avoid_leadership=$1, order_index=$2 WHERE character_id=$3", gm.AvoidLeadership, gm.OrderIndex, gm.CharID)
if err != nil {
logger.Error(
"failed to update guild member data",
zap.Error(err),
zap.Uint32("charID", gm.CharID),
zap.Uint32("guildID", gm.GuildID),
)
return err
}
return nil
}
const guildMembersSelectSQL = `
SELECT * FROM (
SELECT
g.id AS guild_id,
joined_at,
COALESCE((SELECT SUM(souls) FROM festa_submissions fs WHERE fs.character_id=c.id), 0) AS souls,
COALESCE(rp_today, 0) AS rp_today,
COALESCE(rp_yesterday, 0) AS rp_yesterday,
c.name,
c.id AS character_id,
COALESCE(order_index, 0) AS order_index,
c.last_login,
COALESCE(recruiter, false) AS recruiter,
COALESCE(avoid_leadership, false) AS avoid_leadership,
c.hr,
c.gr,
c.weapon_id,
c.weapon_type,
EXISTS(SELECT 1 FROM guild_applications ga WHERE ga.character_id=c.id AND application_type='applied') AS is_applicant,
CASE WHEN g.leader_id = c.id THEN true ELSE false END AS is_leader
FROM guild_characters gc
LEFT JOIN characters c ON c.id = gc.character_id
LEFT JOIN guilds g ON g.id = gc.guild_id
) AS subquery
`
func GetGuildMembers(guildID uint32, applicants bool) ([]*GuildMember, error) {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := db.Queryx(fmt.Sprintf(`
%s
WHERE guild_id = $1 AND is_applicant = $2
`, guildMembersSelectSQL), guildID, applicants)
if err != nil {
logger.Error("failed to retrieve membership data for guild", zap.Error(err), zap.Uint32("guildID", guildID))
return nil, err
}
defer rows.Close()
members := make([]*GuildMember, 0)
for rows.Next() {
member, err := buildGuildMemberObjectFromDBResult(rows, err)
if err != nil {
return nil, err
}
members = append(members, member)
}
return members, nil
}
func GetCharacterGuildData(charID uint32) (*GuildMember, error) {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := db.Queryx(fmt.Sprintf("%s WHERE character_id=$1", guildMembersSelectSQL), charID)
if err != nil {
logger.Error(fmt.Sprintf("failed to retrieve membership data for character '%d'", charID))
return nil, err
}
defer rows.Close()
hasRow := rows.Next()
if !hasRow {
return nil, nil
}
return buildGuildMemberObjectFromDBResult(rows, err)
}
func buildGuildMemberObjectFromDBResult(rows *sqlx.Rows, err error) (*GuildMember, error) {
logger := logger.Get()
memberData := &GuildMember{}
err = rows.StructScan(&memberData)
if err != nil {
logger.Error("failed to retrieve guild data from database", zap.Error(err))
return nil, err
}
return memberData, nil
}

View File

@@ -1,236 +0,0 @@
package service
import (
"database/sql"
"erupe-ce/internal/constant"
"erupe-ce/network/binpacket"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/database"
"erupe-ce/utils/logger"
"fmt"
"time"
"go.uber.org/zap"
)
type Mail struct {
ID int `db:"id"`
SenderID uint32 `db:"sender_id"`
RecipientID uint32 `db:"recipient_id"`
Subject string `db:"subject"`
Body string `db:"body"`
Read bool `db:"read"`
Deleted bool `db:"deleted"`
Locked bool `db:"locked"`
AttachedItemReceived bool `db:"attached_item_received"`
AttachedItemID uint16 `db:"attached_item"`
AttachedItemAmount uint16 `db:"attached_item_amount"`
CreatedAt time.Time `db:"created_at"`
IsGuildInvite bool `db:"is_guild_invite"`
IsSystemMessage bool `db:"is_sys_message"`
SenderName string `db:"sender_name"`
}
func (m *Mail) Send(transaction *sql.Tx) error {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
query := `
INSERT INTO mail (sender_id, recipient_id, subject, body, attached_item, attached_item_amount, is_guild_invite, is_sys_message)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
`
if transaction == nil {
_, err = db.Exec(query, m.SenderID, m.RecipientID, m.Subject, m.Body, m.AttachedItemID, m.AttachedItemAmount, m.IsGuildInvite, m.IsSystemMessage)
} else {
_, err = transaction.Exec(query, m.SenderID, m.RecipientID, m.Subject, m.Body, m.AttachedItemID, m.AttachedItemAmount, m.IsGuildInvite, m.IsSystemMessage)
}
if err != nil {
logger.Error(
"failed to send mail",
zap.Error(err),
zap.Uint32("senderID", m.SenderID),
zap.Uint32("recipientID", m.RecipientID),
zap.String("subject", m.Subject),
zap.String("body", m.Body),
zap.Uint16("itemID", m.AttachedItemID),
zap.Uint16("itemAmount", m.AttachedItemAmount),
zap.Bool("isGuildInvite", m.IsGuildInvite),
zap.Bool("isSystemMessage", m.IsSystemMessage),
)
return err
}
return nil
}
func (m *Mail) MarkRead() error {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
_, err = db.Exec(`
UPDATE mail SET read = true WHERE id = $1
`, m.ID)
if err != nil {
logger.Error(
"failed to mark mail as read",
zap.Error(err),
zap.Int("mailID", m.ID),
)
return err
}
return nil
}
func GetMailListForCharacter(charID uint32) ([]Mail, error) {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := db.Queryx(`
SELECT
m.id,
m.sender_id,
m.recipient_id,
m.subject,
m.read,
m.attached_item_received,
m.attached_item,
m.attached_item_amount,
m.created_at,
m.is_guild_invite,
m.is_sys_message,
m.deleted,
m.locked,
c.name as sender_name
FROM mail m
JOIN characters c ON c.id = m.sender_id
WHERE recipient_id = $1 AND m.deleted = false
ORDER BY m.created_at DESC, id DESC
LIMIT 32
`, charID)
if err != nil {
logger.Error("failed to get mail for character", zap.Error(err), zap.Uint32("charID", charID))
return nil, err
}
defer rows.Close()
allMail := make([]Mail, 0)
for rows.Next() {
mail := Mail{}
err := rows.StructScan(&mail)
if err != nil {
return nil, err
}
allMail = append(allMail, mail)
}
return allMail, nil
}
func GetMailByID(ID int) (*Mail, error) {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
row := db.QueryRowx(`
SELECT
m.id,
m.sender_id,
m.recipient_id,
m.subject,
m.read,
m.body,
m.attached_item_received,
m.attached_item,
m.attached_item_amount,
m.created_at,
m.is_guild_invite,
m.is_sys_message,
m.deleted,
m.locked,
c.name as sender_name
FROM mail m
JOIN characters c ON c.id = m.sender_id
WHERE m.id = $1
LIMIT 1
`, ID)
mail := &Mail{}
err = row.StructScan(mail)
if err != nil {
logger.Error(
"failed to retrieve mail",
zap.Error(err),
zap.Int("mailID", ID),
)
return nil, err
}
return mail, nil
}
type SessionMail interface {
QueueSendMHFLazy(packet mhfpacket.MHFPacket)
}
func SendMailNotification(m *Mail, recipient SessionMail) {
bf := byteframe.NewByteFrame()
notification := &binpacket.MsgBinMailNotify{
SenderName: getCharacterName(m.SenderID),
}
notification.Build(bf)
castedBinary := &mhfpacket.MsgSysCastedBinary{
CharID: m.SenderID,
BroadcastType: 0x00,
MessageType: constant.BinaryMessageTypeMailNotify,
RawDataPayload: bf.Data(),
}
castedBinary.Build(bf)
recipient.QueueSendMHFLazy(castedBinary)
}
func getCharacterName(charID uint32) string {
db, err := database.GetDB()
logger := logger.Get()
if err != nil {
logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
row := db.QueryRow("SELECT name FROM characters WHERE id = $1", charID)
charName := ""
err = row.Scan(&charName)
if err != nil {
return ""
}
return charName
}

306
main.go
View File

@@ -1,30 +1,37 @@
package main
import (
"erupe-ce/config"
_config "erupe-ce/config"
"fmt"
"net"
"os"
"os/signal"
"runtime/debug"
"slices"
"syscall"
"time"
"erupe-ce/server/api"
"erupe-ce/server/channelserver"
"erupe-ce/server/discordbot"
"erupe-ce/server/entrance"
"erupe-ce/server/sign"
"erupe-ce/utils/database"
"erupe-ce/utils/logger"
"erupe-ce/utils/gametime"
"erupe-ce/server/entranceserver"
"erupe-ce/server/signserver"
"erupe-ce/server/signv2server"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
"go.uber.org/zap"
)
var mainLogger logger.Logger
// Temporary DB auto clean on startup for quick development & testing.
func cleanDB(db *sqlx.DB, config *_config.Config) {
_ = db.MustExec("DELETE FROM guild_characters")
_ = db.MustExec("DELETE FROM guilds")
_ = db.MustExec("DELETE FROM characters")
if config.ProxyPort == 0 {
_ = db.MustExec("DELETE FROM sign_sessions")
}
_ = db.MustExec("DELETE FROM users")
}
var Commit = func() string {
if info, ok := debug.ReadBuildInfo(); ok {
@@ -37,122 +44,223 @@ var Commit = func() string {
return "unknown"
}
func initLogger() {
var zapLogger *zap.Logger
zapLogger, _ = zap.NewDevelopment(zap.WithCaller(false))
defer zapLogger.Sync()
// Initialize the global logger
logger.Init(zapLogger)
mainLogger = logger.Get().Named("main")
}
func main() {
var err error
config := config.GetConfig()
initLogger()
mainLogger.Info(fmt.Sprintf("Starting Erupe (9.3b-%s)", Commit()))
mainLogger.Info(fmt.Sprintf("Client Mode: %s (%d)", config.ClientMode, config.ClientID))
checkAndExitIf(config.Database.Password == "", "Database password is blank")
resolveHostIP()
discordBot := initializeDiscordBot()
database, err := database.InitDB(config)
if err != nil {
mainLogger.Fatal(fmt.Sprintf("Database initialization failed: %s", err))
var zapLogger *zap.Logger
config := _config.ErupeConfig
if config.DevMode {
zapLogger, _ = zap.NewDevelopment()
} else {
zapLogger, _ = zap.NewProduction()
}
mainLogger.Info(fmt.Sprintf("Server Time: %s", gametime.TimeAdjusted().String()))
defer zapLogger.Sync()
logger := zapLogger.Named("main")
logger.Info(fmt.Sprintf("Starting Erupe (9.3b-%s)", Commit()))
logger.Info(fmt.Sprintf("Client Mode: %s (%d)", config.ClientMode, config.RealClientMode))
if config.Database.Password == "" {
preventClose("Database password is blank")
}
if net.ParseIP(config.Host) == nil {
ips, _ := net.LookupIP(config.Host)
for _, ip := range ips {
if ip != nil {
config.Host = ip.String()
break
}
}
if net.ParseIP(config.Host) == nil {
preventClose("Invalid host address")
}
}
// Discord bot
var discordBot *discordbot.DiscordBot = nil
if config.Discord.Enabled {
bot, err := discordbot.NewDiscordBot(discordbot.Options{
Logger: logger,
Config: _config.ErupeConfig,
})
if err != nil {
preventClose(fmt.Sprintf("Discord: Failed to start, %s", err.Error()))
}
// Discord bot
err = bot.Start()
if err != nil {
preventClose(fmt.Sprintf("Discord: Failed to start, %s", err.Error()))
}
discordBot = bot
logger.Info("Discord: Started successfully")
} else {
logger.Info("Discord: Disabled")
}
// Create the postgres DB pool.
connectString := fmt.Sprintf(
"host='%s' port='%d' user='%s' password='%s' dbname='%s' sslmode=disable",
config.Database.Host,
config.Database.Port,
config.Database.User,
config.Database.Password,
config.Database.Database,
)
db, err := sqlx.Open("postgres", connectString)
if err != nil {
preventClose(fmt.Sprintf("Database: Failed to open, %s", err.Error()))
}
// Test the DB connection.
err = db.Ping()
if err != nil {
preventClose(fmt.Sprintf("Database: Failed to ping, %s", err.Error()))
}
logger.Info("Database: Started successfully")
// Clear stale data
if config.ProxyPort == 0 {
_ = db.MustExec("DELETE FROM sign_sessions")
}
_ = db.MustExec("DELETE FROM servers")
_ = db.MustExec(`UPDATE guild_characters SET treasure_hunt=NULL`)
// Clean the DB if the option is on.
if config.DevMode && config.DevModeOptions.CleanDB {
logger.Info("Database: Started clearing...")
cleanDB(db, config)
logger.Info("Database: Finished clearing")
}
logger.Info(fmt.Sprintf("Server Time: %s", channelserver.TimeAdjusted().String()))
// Now start our server(s).
// Entrance server.
var entranceServer *entrance.EntranceServer
var entranceServer *entranceserver.Server
if config.Entrance.Enabled {
entranceServer = entrance.NewServer()
entranceServer = entranceserver.NewServer(
&entranceserver.Config{
Logger: logger.Named("entrance"),
ErupeConfig: config,
DB: db,
})
err = entranceServer.Start()
if err != nil {
preventClose(fmt.Sprintf("Entrance: Failed to start, %s", err.Error()))
}
mainLogger.Info("Entrance: Started successfully")
logger.Info("Entrance: Started successfully")
} else {
mainLogger.Info("Entrance: Disabled")
logger.Info("Entrance: Disabled")
}
// Sign server.
var signServer *sign.SignServer
var signServer *signserver.Server
if config.Sign.Enabled {
signServer = sign.NewServer()
signServer = signserver.NewServer(
&signserver.Config{
Logger: logger.Named("sign"),
ErupeConfig: config,
DB: db,
})
err = signServer.Start()
if err != nil {
preventClose(fmt.Sprintf("Sign: Failed to start, %s", err.Error()))
}
mainLogger.Info("Sign: Started successfully")
logger.Info("Sign: Started successfully")
} else {
mainLogger.Info("Sign: Disabled")
logger.Info("Sign: Disabled")
}
// Api server
var apiServer *api.APIServer
if config.API.Enabled {
apiServer = api.NewAPIServer()
err = apiServer.Start()
// New Sign server
var newSignServer *signv2server.Server
if config.SignV2.Enabled {
newSignServer = signv2server.NewServer(
&signv2server.Config{
Logger: logger.Named("sign"),
ErupeConfig: config,
DB: db,
})
err = newSignServer.Start()
if err != nil {
preventClose(fmt.Sprintf("API: Failed to start, %s", err.Error()))
preventClose(fmt.Sprintf("SignV2: Failed to start, %s", err.Error()))
}
mainLogger.Info("API: Started successfully")
logger.Info("SignV2: Started successfully")
} else {
mainLogger.Info("API: Disabled")
logger.Info("SignV2: Disabled")
}
var channelServers []*channelserver.ChannelServer
var worlds [][]*channelserver.Server
var ports []uint16
if config.Channel.Enabled {
channelQuery := ""
si := 0
ci := 0
count := 1
for j, ee := range config.Entrance.Entries {
for i, ce := range ee.Channels {
sid := (4096 + si*256) + (16 + ci)
var count int
for j, ee := range config.Channel.Worlds {
var lands []*channelserver.Server
for i, ce := range ee.Lands {
sid := (4096 + j*256) + (16 + i)
c := *channelserver.NewServer(&channelserver.Config{
ID: uint16(sid),
DiscordBot: discordBot,
ID: uint16(sid),
Logger: logger.Named("channel-" + fmt.Sprint(count+1)),
ErupeConfig: config,
DB: db,
DiscordBot: discordBot,
})
if ee.IP == "" {
c.IP = config.Host
} else {
c.IP = ee.IP
}
c.Port = ce.Port
if ce.Port == 0 {
for i := 0; ; i++ {
port := uint16(54001 + i)
if !slices.Contains(ports, port) {
ce.Port = port
break
}
}
}
if slices.Contains(ports, ce.Port) {
preventClose("Channel: Failed to start, duplicate port")
break
} else {
ports = append(ports, ce.Port)
c.Port = ce.Port
}
c.GlobalID = fmt.Sprintf("%02d%02d", j+1, i+1)
err = c.Start()
if err != nil {
preventClose(fmt.Sprintf("Channel: Failed to start, %s", err.Error()))
} else {
channelQuery += fmt.Sprintf(`INSERT INTO servers (server_id, current_players, world_name, world_description, land) VALUES (%d, 0, '%s', '%s', %d);`, sid, ee.Name, ee.Description, i+1)
channelServers = append(channelServers, &c)
mainLogger.Info(fmt.Sprintf("Channel %d (%d): Started successfully", count, ce.Port))
ci++
lands = append(lands, &c)
logger.Info(fmt.Sprintf("Channel %d (%d): Started successfully", count, c.Port))
count++
}
}
ci = 0
si++
worlds = append(worlds, lands)
}
// Register all servers in DB
_ = database.MustExec(channelQuery)
_ = db.MustExec(channelQuery)
for _, c := range channelServers {
c.Channels = channelServers
if config.Entrance.Enabled {
entranceServer.SetWorlds(worlds)
}
}
mainLogger.Info("Finished starting Erupe")
logger.Info("Finished starting Erupe")
// Wait for exit or interrupt with ctrl+C.
c := make(chan os.Signal, 1)
@@ -162,17 +270,21 @@ func main() {
if !config.DisableSoftCrash {
for i := 0; i < 10; i++ {
message := fmt.Sprintf("Shutting down in %d...", 10-i)
for _, channelServer := range channelServers {
channelServer.BroadcastChatMessage(message)
for _, w := range worlds {
for _, l := range w {
l.BroadcastChatMessage(message)
}
}
mainLogger.Warn(message)
logger.Info(message)
time.Sleep(time.Second)
}
}
if config.Channel.Enabled {
for _, channelServer := range channelServers {
channelServer.Shutdown()
for _, w := range worlds {
for _, l := range w {
l.Shutdown()
}
}
}
@@ -180,8 +292,8 @@ func main() {
signServer.Shutdown()
}
if config.API.Enabled {
apiServer.Shutdown()
if config.SignV2.Enabled {
newSignServer.Shutdown()
}
if config.Entrance.Enabled {
@@ -198,46 +310,12 @@ func wait() {
}
func preventClose(text string) {
if config.GetConfig().DisableSoftCrash {
if _config.ErupeConfig.DisableSoftCrash {
os.Exit(0)
}
mainLogger.Error(fmt.Sprintf(("\nFailed to start Erupe:\n" + text)))
fmt.Println("\nFailed to start Erupe:\n" + text)
go wait()
mainLogger.Error(fmt.Sprintf(("\nPress Enter/Return to exit...")))
fmt.Println("\nPress Enter/Return to exit...")
fmt.Scanln()
os.Exit(0)
}
func checkAndExitIf(condition bool, message string) {
if condition {
preventClose(message)
}
}
func resolveHostIP() {
if net.ParseIP(config.GetConfig().Host) == nil {
ips, err := net.LookupIP(config.GetConfig().Host)
if err == nil && len(ips) > 0 {
config.GetConfig().Host = ips[0].String()
}
checkAndExitIf(net.ParseIP(config.GetConfig().Host) == nil, "Invalid host address")
}
}
func initializeDiscordBot() *discordbot.DiscordBot {
if !config.GetConfig().Discord.Enabled {
mainLogger.Info("Discord: Disabled")
return nil
}
bot, err := discordbot.NewDiscordBot()
checkAndExitIf(err != nil, fmt.Sprintf("Discord: Failed to start, %s", err))
err = bot.Start()
checkAndExitIf(err != nil, fmt.Sprintf("Discord: Failed to start, %s", err))
_, err = bot.Session.ApplicationCommandBulkOverwrite(bot.Session.State.User.ID, "", discordbot.Commands)
checkAndExitIf(err != nil, fmt.Sprintf("Discord: Failed to register commands, %s", err))
mainLogger.Info("Discord: Started successfully")
return bot
}

View File

@@ -1,9 +1,9 @@
package binpacket
import (
"erupe-ce/common/byteframe"
"erupe-ce/common/stringsupport"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/stringsupport"
)
// ChatType represents the chat message type (Thanks to @Alice on discord for identifying these!)
@@ -11,8 +11,7 @@ type ChatType uint8
// Chat types
const (
ChatTypeWorld ChatType = 0
ChatTypeStage = 1
ChatTypeLocal ChatType = 1
ChatTypeGuild = 2
ChatTypeAlliance = 3
ChatTypeParty = 4

View File

@@ -1,9 +1,9 @@
package binpacket
import (
"erupe-ce/common/byteframe"
"erupe-ce/common/stringsupport"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/stringsupport"
)
type MsgBinMailNotify struct {

View File

@@ -1,8 +1,8 @@
package binpacket
import (
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
)
// MsgBinTargeted is a format used for some broadcast types

View File

@@ -0,0 +1,4 @@
package clientctx
// ClientContext holds contextual data required for packet encoding/decoding.
type ClientContext struct{} // Unused

View File

@@ -3,11 +3,12 @@ package network
import (
"encoding/hex"
"errors"
"erupe-ce/config"
"erupe-ce/network/crypto"
_config "erupe-ce/config"
"fmt"
"io"
"net"
"erupe-ce/network/crypto"
)
// CryptConn represents a MHF encrypted two-way connection,
@@ -51,7 +52,7 @@ func (cc *CryptConn) ReadPacket() ([]byte, error) {
var encryptedPacketBody []byte
// Don't know when support for this was added, works in Forward.4, doesn't work in Season 6.0
if config.GetConfig().ClientID < config.F1 {
if _config.ErupeConfig.RealClientMode < _config.F1 {
encryptedPacketBody = make([]byte, cph.DataSize)
} else {
encryptedPacketBody = make([]byte, uint32(cph.DataSize)+(uint32(cph.Pf0-0x03)*0x1000))
@@ -66,7 +67,7 @@ func (cc *CryptConn) ReadPacket() ([]byte, error) {
cc.readKeyRot = uint32(cph.KeyRotDelta) * (cc.readKeyRot + 1)
}
out, combinedCheck, check0, check1, check2 := crypto.Crypto(encryptedPacketBody, cc.readKeyRot, false, nil)
out, combinedCheck, check0, check1, check2 := crypto.Decrypt(encryptedPacketBody, cc.readKeyRot, nil)
if cph.Check0 != check0 || cph.Check1 != check1 || cph.Check2 != check2 {
fmt.Printf("got c0 %X, c1 %X, c2 %X\n", check0, check1, check2)
fmt.Printf("want c0 %X, c1 %X, c2 %X\n", cph.Check0, cph.Check1, cph.Check2)
@@ -76,7 +77,7 @@ func (cc *CryptConn) ReadPacket() ([]byte, error) {
// Attempt to bruteforce it.
fmt.Println("Crypto out of sync? Attempting bruteforce")
for key := byte(0); key < 255; key++ {
out, combinedCheck, check0, check1, check2 = crypto.Crypto(encryptedPacketBody, 0, false, &key)
out, combinedCheck, check0, check1, check2 = crypto.Decrypt(encryptedPacketBody, 0, &key)
//fmt.Printf("Key: 0x%X\n%s\n", key, hex.Dump(out))
if cph.Check0 == check0 && cph.Check1 == check1 && cph.Check2 == check2 {
fmt.Printf("Bruceforce successful, override key: 0x%X\n", key)
@@ -105,7 +106,7 @@ func (cc *CryptConn) SendPacket(data []byte) error {
}
// Encrypt the data
encData, combinedCheck, check0, check1, check2 := crypto.Crypto(data, cc.sendKeyRot, true, nil)
encData, combinedCheck, check0, check1, check2 := crypto.Encrypt(data, cc.sendKeyRot, nil)
header := &CryptPacketHeader{}
header.Pf0 = byte(((uint(len(encData)) >> 12) & 0xF3) | 3)
@@ -122,7 +123,9 @@ func (cc *CryptConn) SendPacket(data []byte) error {
return err
}
cc.conn.Write(append(headerBytes, encData...))
cc.conn.Write(headerBytes)
cc.conn.Write(encData)
cc.sentPackets++
cc.prevSendPacketCombinedCheck = combinedCheck

View File

@@ -6,30 +6,46 @@ var (
_sharedCryptKey = []byte{0xDD, 0xA8, 0x5F, 0x1E, 0x57, 0xAF, 0xC0, 0xCC, 0x43, 0x35, 0x8F, 0xBB, 0x6F, 0xE6, 0xA1, 0xD6, 0x60, 0xB9, 0x1A, 0xAE, 0x20, 0x49, 0x24, 0x81, 0x21, 0xFE, 0x86, 0x2B, 0x98, 0xB7, 0xB3, 0xD2, 0x91, 0x01, 0x3A, 0x4C, 0x65, 0x92, 0x1C, 0xF4, 0xBE, 0xDD, 0xD9, 0x08, 0xE6, 0x81, 0x98, 0x1B, 0x8D, 0x60, 0xF3, 0x6F, 0xA1, 0x47, 0x24, 0xF1, 0x53, 0x45, 0xC8, 0x7B, 0x88, 0x80, 0x4E, 0x36, 0xC3, 0x0D, 0xC9, 0xD6, 0x8B, 0x08, 0x19, 0x0B, 0xA5, 0xC1, 0x11, 0x4C, 0x60, 0xF8, 0x5D, 0xFC, 0x15, 0x68, 0x7E, 0x32, 0xC0, 0x50, 0xAB, 0x64, 0x1F, 0x8A, 0xD4, 0x08, 0x39, 0x7F, 0xC2, 0xFB, 0xBA, 0x6C, 0xF0, 0xE6, 0xB0, 0x31, 0x10, 0xC1, 0xBF, 0x75, 0x43, 0xBB, 0x18, 0x04, 0x0D, 0xD1, 0x97, 0xF7, 0x23, 0x21, 0x83, 0x8B, 0xCA, 0x25, 0x2B, 0xA3, 0x03, 0x13, 0xEA, 0xAE, 0xFE, 0xF0, 0xEB, 0xFD, 0x85, 0x57, 0x53, 0x65, 0x41, 0x2A, 0x40, 0x99, 0xC0, 0x94, 0x65, 0x7E, 0x7C, 0x93, 0x82, 0xB0, 0xB3, 0xE5, 0xC0, 0x21, 0x09, 0x84, 0xD5, 0xEF, 0x9F, 0xD1, 0x7E, 0xDC, 0x4D, 0xF5, 0x7E, 0xCD, 0x45, 0x3C, 0x7F, 0xF5, 0x59, 0x98, 0xC6, 0x55, 0xFC, 0x9F, 0xA3, 0xB7, 0x74, 0xEE, 0x31, 0x98, 0xE6, 0xB7, 0xBE, 0x26, 0xF4, 0x3C, 0x76, 0xF1, 0x23, 0x7E, 0x02, 0x4E, 0x3C, 0xD1, 0xC7, 0x28, 0x23, 0x73, 0xC4, 0xD9, 0x5E, 0x0D, 0xA1, 0x80, 0xA5, 0xAA, 0x26, 0x0A, 0xA3, 0x44, 0x82, 0x74, 0xE6, 0x3C, 0x44, 0x27, 0x51, 0x0D, 0x5F, 0xC7, 0x9C, 0xD6, 0x63, 0x67, 0xA5, 0x27, 0x97, 0x38, 0xFB, 0x2D, 0xD3, 0xD6, 0x60, 0x25, 0x83, 0x4D, 0x37, 0x5B, 0x40, 0x59, 0x11, 0x77, 0x51, 0x11, 0x14, 0x18, 0x07, 0x63, 0xB1, 0x34, 0x3D, 0xB8, 0x60, 0x13, 0xC2, 0xE8, 0x13, 0x82}
)
// Crypto is a generalized MHF crypto function that can perform both encryption and decryption,
// Encrypt encrypts the given data using MHF's custom encryption+checksum method.
// if a overrideByteKey value is supplied (!= nil), it will be used to override the derived/truncated key byte.
func Encrypt(data []byte, key uint32, overrideByteKey *byte) (outputData []byte, combinedCheck uint16, check0 uint16, check1 uint16, check2 uint16) {
return _generalCrypt(data, key, 0, overrideByteKey)
}
// Decrypt decrypts the given data using MHF's custom decryption+checksum method.
// if a overrideByteKey value is supplied (!= nil), it will be used to override the derived/truncated key byte.
func Decrypt(data []byte, key uint32, overrideByteKey *byte) (outputData []byte, combinedCheck uint16, check0 uint16, check1 uint16, check2 uint16) {
return _generalCrypt(data, key, 1, overrideByteKey)
}
// _generalCrypt is a generalized MHF crypto function that can perform both encryption and decryption,
// these two crypto operations are combined into a single function because they shared most of their logic.
func Crypto(data []byte, rotKey uint32, encrypt bool, overrideByteKey *byte) ([]byte, uint16, uint16, uint16, uint16) {
// encrypt: cryptType==0
// decrypt: cryptType==1
func _generalCrypt(data []byte, rotKey uint32, cryptType int, overrideByteKey *byte) ([]byte, uint16, uint16, uint16, uint16) {
cryptKeyTruncByte := byte(((rotKey >> 1) % 999983) & 0xFF)
if overrideByteKey != nil {
cryptKeyTruncByte = *overrideByteKey
}
derivedCryptKey := (uint32(len(data)) * (uint32(cryptKeyTruncByte) + 1)) & 0xFFFFFFFF
derivedCryptKey := int32((uint32(len(data)) * (uint32(cryptKeyTruncByte) + 1)) & 0xFFFFFFFF)
sharedBufIdx := byte(1)
var accumulator0, accumulator1, accumulator2 uint32
accumulator0 := uint32(0)
accumulator1 := uint32(0)
accumulator2 := uint32(0)
var outputData []byte
if encrypt {
if cryptType == 0 {
for i := 0; i < len(data); i++ {
// Do the encryption for this iteration
encKeyIdx := ((derivedCryptKey >> 10) ^ uint32(data[i])) & 0xFF
derivedCryptKey = 1277*derivedCryptKey + 1277
encKeyIdx := int32(((uint32(derivedCryptKey) >> 10) ^ uint32(data[i])) & 0xFF)
derivedCryptKey = (0x4FD * (derivedCryptKey + 1))
encKeyByte := _encryptKey[encKeyIdx]
// Update the checksum accumulators.
accumulator2 = accumulator2 + (uint32(sharedBufIdx) * uint32(data[i]))
accumulator1 = accumulator1 + encKeyIdx
accumulator0 = accumulator0 + uint32(encKeyByte)<<(i&7)
accumulator2 = uint32((accumulator2 + (uint32(sharedBufIdx) * uint32(data[i]))) & 0xFFFFFFFF)
accumulator1 = uint32((accumulator1 + uint32(encKeyIdx)) & 0xFFFFFFFF)
accumulator0 = uint32((accumulator0 + (uint32(encKeyByte)<<(i&7))&0xFFFFFFFF) & 0xFFFFFFFF)
// Append the output.
outputData = append(outputData, _sharedCryptKey[sharedBufIdx]^encKeyByte)
@@ -37,32 +53,32 @@ func Crypto(data []byte, rotKey uint32, encrypt bool, overrideByteKey *byte) ([]
// Update the sharedBufIdx for the next iteration.
sharedBufIdx = data[i]
}
} else {
} else if cryptType == 1 {
for i := 0; i < len(data); i++ {
// Do the decryption for this iteration
oldSharedBufIdx := sharedBufIdx
tIdx := data[i] ^ _sharedCryptKey[sharedBufIdx]
decKeyByte := _decryptKey[tIdx]
sharedBufIdx = byte((derivedCryptKey >> 10) ^ uint32(decKeyByte))
sharedBufIdx = byte(((uint32(derivedCryptKey) >> 10) ^ uint32(decKeyByte)) & 0xFF)
// Update the checksum accumulators.
accumulator0 = accumulator0 + uint32(tIdx)<<(i&7)
accumulator1 = accumulator1 + uint32(decKeyByte)
accumulator2 = accumulator2 + uint32(oldSharedBufIdx)*uint32(sharedBufIdx)
accumulator0 = (accumulator0 + ((uint32(tIdx) << (i & 7)) & 0xFFFFFFFF))
accumulator1 = (accumulator1 + uint32(decKeyByte)) & 0xFFFFFFFF
accumulator2 = (accumulator2 + ((uint32(oldSharedBufIdx) * uint32(sharedBufIdx)) & 0xFFFFFFFF)) & 0xFFFFFFFF
// Append the output.
outputData = append(outputData, sharedBufIdx)
// Update the key pos for next iteration.
derivedCryptKey = 1277*derivedCryptKey + 1277
derivedCryptKey = (0x4FD * (derivedCryptKey + 1))
}
}
var check [4]uint16
check[0] = uint16(accumulator1 + (accumulator0 >> 1) + (accumulator2 >> 2))
check[1] = uint16(accumulator0 ^ ((accumulator0 & 0xFFFF0000) >> 16))
check[2] = uint16(accumulator1 ^ ((accumulator1 & 0xFFFF0000) >> 16))
check[3] = uint16(accumulator2 ^ ((accumulator2 & 0xFFFF0000) >> 16))
combinedCheck := uint16((accumulator1 + (accumulator0 >> 1) + (accumulator2 >> 2)) & 0xFFFF)
check0 := uint16((accumulator0 ^ ((accumulator0 & 0xFFFF0000) >> 16)) & 0xFFFF)
check1 := uint16((accumulator1 ^ ((accumulator1 & 0xFFFF0000) >> 16)) & 0xFFFF)
check2 := uint16((accumulator2 ^ ((accumulator2 & 0xFFFF0000) >> 16)) & 0xFFFF)
return outputData, check[0], check[1], check[2], check[3]
return outputData, combinedCheck, check0, check1, check2
}

View File

@@ -65,7 +65,7 @@ func TestEncrypt(t *testing.T) {
for k, tt := range tests {
testname := fmt.Sprintf("encrypt_test_%d", k)
t.Run(testname, func(t *testing.T) {
out, cc, c0, c1, c2 := Crypto(tt.decryptedData, tt.key, true, nil)
out, cc, c0, c1, c2 := Encrypt(tt.decryptedData, tt.key, nil)
if cc != tt.ecc {
t.Errorf("got cc 0x%X, want 0x%X", cc, tt.ecc)
} else if c0 != tt.ec0 {
@@ -86,7 +86,7 @@ func TestDecrypt(t *testing.T) {
for k, tt := range tests {
testname := fmt.Sprintf("decrypt_test_%d", k)
t.Run(testname, func(t *testing.T) {
out, cc, c0, c1, c2 := Crypto(tt.decryptedData, tt.key, false, nil)
out, cc, c0, c1, c2 := Decrypt(tt.encryptedData, tt.key, nil)
if cc != tt.ecc {
t.Errorf("got cc 0x%X, want 0x%X", cc, tt.ecc)
} else if c0 != tt.ec0 {

View File

@@ -1,18 +1,19 @@
package mhfpacket
import (
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
)
// Parser is the interface that wraps the Parse method.
type Parser interface {
Parse(bf *byteframe.ByteFrame) error
Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error
}
// Builder is the interface that wraps the Build method.
type Builder interface {
Build(bf *byteframe.ByteFrame) error
Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error
}
// Opcoder is the interface that wraps the Opcode method.

View File

@@ -3,8 +3,9 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
)
// MsgCaExchangeItem represents the MSG_CA_EXCHANGE_ITEM
@@ -16,11 +17,11 @@ func (m *MsgCaExchangeItem) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgCaExchangeItem) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgCaExchangeItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}
// Build builds a binary packet from the current data.
func (m *MsgCaExchangeItem) Build(bf *byteframe.ByteFrame) error {
func (m *MsgCaExchangeItem) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -3,8 +3,10 @@ package mhfpacket
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/common/byteframe"
)
// MsgHead represents the MSG_HEAD
@@ -16,11 +18,11 @@ func (m *MsgHead) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgHead) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgHead) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}
// Build builds a binary packet from the current data.
func (m *MsgHead) Build(bf *byteframe.ByteFrame) error {
func (m *MsgHead) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -3,8 +3,10 @@ package mhfpacket
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/common/byteframe"
)
// MsgMhfAcceptReadReward represents the MSG_MHF_ACCEPT_READ_REWARD
@@ -16,11 +18,11 @@ func (m *MsgMhfAcceptReadReward) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAcceptReadReward) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcceptReadReward) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAcceptReadReward) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcceptReadReward) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -2,10 +2,11 @@ package mhfpacket
import (
"errors"
"erupe-ce/config"
_config "erupe-ce/config"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
)
// MsgMhfAcquireCafeItem represents the MSG_MHF_ACQUIRE_CAFE_ITEM
@@ -25,12 +26,12 @@ func (m *MsgMhfAcquireCafeItem) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAcquireCafeItem) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireCafeItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.ItemType = bf.ReadUint16()
m.ItemID = bf.ReadUint16()
m.Quant = bf.ReadUint16()
if config.GetConfig().ClientID >= config.G1 {
if _config.ErupeConfig.RealClientMode >= _config.G1 {
m.PointCost = bf.ReadUint32()
} else {
m.PointCost = uint32(bf.ReadUint16())
@@ -40,6 +41,6 @@ func (m *MsgMhfAcquireCafeItem) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAcquireCafeItem) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireCafeItem) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -4,14 +4,15 @@ import (
"errors"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
"erupe-ce/common/byteframe"
)
// MsgMhfAcquireDistItem represents the MSG_MHF_ACQUIRE_DIST_ITEM
type MsgMhfAcquireDistItem struct {
AckHandle uint32
AckHandle uint32
DistributionType uint8
DistributionID uint32
DistributionID uint32
}
// Opcode returns the ID associated with this packet type.
@@ -20,7 +21,7 @@ func (m *MsgMhfAcquireDistItem) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAcquireDistItem) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireDistItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.DistributionType = bf.ReadUint8()
m.DistributionID = bf.ReadUint32()
@@ -28,6 +29,6 @@ func (m *MsgMhfAcquireDistItem) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAcquireDistItem) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireDistItem) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -2,7 +2,8 @@ package mhfpacket
import (
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
"erupe-ce/common/byteframe"
)
// MsgMhfAcquireExchangeShop represents the MSG_MHF_ACQUIRE_EXCHANGE_SHOP
@@ -18,7 +19,7 @@ func (m *MsgMhfAcquireExchangeShop) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAcquireExchangeShop) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireExchangeShop) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.DataSize = bf.ReadUint16()
m.RawDataPayload = bf.ReadBytes(uint(m.DataSize))
@@ -26,7 +27,7 @@ func (m *MsgMhfAcquireExchangeShop) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAcquireExchangeShop) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireExchangeShop) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
bf.WriteUint32(m.AckHandle)
bf.WriteUint16(m.DataSize)
bf.WriteBytes(m.RawDataPayload)

View File

@@ -3,8 +3,9 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
)
// MsgMhfAcquireFesta represents the MSG_MHF_ACQUIRE_FESTA
@@ -21,7 +22,7 @@ func (m *MsgMhfAcquireFesta) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAcquireFesta) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireFesta) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.FestaID = bf.ReadUint32()
m.GuildID = bf.ReadUint32()
@@ -31,6 +32,6 @@ func (m *MsgMhfAcquireFesta) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAcquireFesta) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireFesta) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -4,13 +4,14 @@ import (
"errors"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
"erupe-ce/common/byteframe"
)
// MsgMhfAcquireFestaIntermediatePrize represents the MSG_MHF_ACQUIRE_FESTA_INTERMEDIATE_PRIZE
type MsgMhfAcquireFestaIntermediatePrize struct {
AckHandle uint32
PrizeID uint32
PrizeID uint32
}
// Opcode returns the ID associated with this packet type.
@@ -19,13 +20,13 @@ func (m *MsgMhfAcquireFestaIntermediatePrize) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAcquireFestaIntermediatePrize) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireFestaIntermediatePrize) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.PrizeID = bf.ReadUint32()
m.PrizeID = bf.ReadUint32()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAcquireFestaIntermediatePrize) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireFestaIntermediatePrize) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -1,16 +1,17 @@
package mhfpacket
import (
"errors"
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/common/byteframe"
)
// MsgMhfAcquireFestaPersonalPrize represents the MSG_MHF_ACQUIRE_FESTA_PERSONAL_PRIZE
type MsgMhfAcquireFestaPersonalPrize struct {
AckHandle uint32
PrizeID uint32
AckHandle uint32
PrizeID uint32
}
// Opcode returns the ID associated with this packet type.
@@ -19,13 +20,13 @@ func (m *MsgMhfAcquireFestaPersonalPrize) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAcquireFestaPersonalPrize) Parse(bf *byteframe.ByteFrame) error {
m.AckHandle = bf.ReadUint32()
m.PrizeID = bf.ReadUint32()
return nil
func (m *MsgMhfAcquireFestaPersonalPrize) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.PrizeID = bf.ReadUint32()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAcquireFestaPersonalPrize) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireFestaPersonalPrize) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -1,16 +1,17 @@
package mhfpacket
import (
"errors"
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/common/byteframe"
)
// MsgMhfAcquireGuildAdventure represents the MSG_MHF_ACQUIRE_GUILD_ADVENTURE
type MsgMhfAcquireGuildAdventure struct {
AckHandle uint32
ID uint32
AckHandle uint32
ID uint32
}
// Opcode returns the ID associated with this packet type.
@@ -19,13 +20,13 @@ func (m *MsgMhfAcquireGuildAdventure) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAcquireGuildAdventure) Parse(bf *byteframe.ByteFrame) error {
m.AckHandle = bf.ReadUint32()
m.ID = bf.ReadUint32()
return nil
func (m *MsgMhfAcquireGuildAdventure) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.ID = bf.ReadUint32()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAcquireGuildAdventure) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireGuildAdventure) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -3,8 +3,9 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
)
// MsgMhfAcquireGuildTresure represents the MSG_MHF_ACQUIRE_GUILD_TRESURE
@@ -20,7 +21,7 @@ func (m *MsgMhfAcquireGuildTresure) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAcquireGuildTresure) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireGuildTresure) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.HuntID = bf.ReadUint32()
m.Unk = bf.ReadBool()
@@ -28,6 +29,6 @@ func (m *MsgMhfAcquireGuildTresure) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAcquireGuildTresure) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireGuildTresure) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -1,10 +1,11 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/common/byteframe"
)
// MsgMhfAcquireGuildTresureSouvenir represents the MSG_MHF_ACQUIRE_GUILD_TRESURE_SOUVENIR
@@ -18,12 +19,12 @@ func (m *MsgMhfAcquireGuildTresureSouvenir) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAcquireGuildTresureSouvenir) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireGuildTresureSouvenir) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAcquireGuildTresureSouvenir) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireGuildTresureSouvenir) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -3,8 +3,9 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
)
// MsgMhfAcquireItem represents the MSG_MHF_ACQUIRE_ITEM
@@ -21,7 +22,7 @@ func (m *MsgMhfAcquireItem) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAcquireItem) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
m.Length = bf.ReadUint16()
@@ -32,6 +33,6 @@ func (m *MsgMhfAcquireItem) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAcquireItem) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireItem) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -3,8 +3,9 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
)
// MsgMhfAcquireMonthlyItem represents the MSG_MHF_ACQUIRE_MONTHLY_ITEM
@@ -22,7 +23,7 @@ func (m *MsgMhfAcquireMonthlyItem) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAcquireMonthlyItem) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireMonthlyItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8()
m.Unk1 = bf.ReadUint8()
@@ -33,6 +34,6 @@ func (m *MsgMhfAcquireMonthlyItem) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAcquireMonthlyItem) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireMonthlyItem) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -1,10 +1,11 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/common/byteframe"
)
// MsgMhfAcquireMonthlyReward represents the MSG_MHF_ACQUIRE_MONTHLY_REWARD
@@ -18,12 +19,12 @@ func (m *MsgMhfAcquireMonthlyReward) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAcquireMonthlyReward) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireMonthlyReward) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAcquireMonthlyReward) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireMonthlyReward) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -3,8 +3,9 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
)
// MsgMhfAcquireTitle represents the MSG_MHF_ACQUIRE_TITLE
@@ -19,7 +20,7 @@ func (m *MsgMhfAcquireTitle) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAcquireTitle) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireTitle) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
titles := int(bf.ReadUint16())
bf.ReadUint16() // Zeroed
@@ -30,6 +31,6 @@ func (m *MsgMhfAcquireTitle) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAcquireTitle) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireTitle) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -3,8 +3,9 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
)
// MsgMhfAcquireTournament represents the MSG_MHF_ACQUIRE_TOURNAMENT
@@ -19,13 +20,13 @@ func (m *MsgMhfAcquireTournament) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAcquireTournament) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireTournament) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.TournamentID = bf.ReadUint32()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAcquireTournament) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireTournament) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -1,29 +1,30 @@
package mhfpacket
import (
"errors"
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/common/byteframe"
)
// MsgMhfAcquireUdItem represents the MSG_MHF_ACQUIRE_UD_ITEM
type MsgMhfAcquireUdItem struct {
AckHandle uint32
Unk0 uint8
// from gal
// daily = 0
// personal = 1
// personal rank = 2
// guild rank = 3
// gcp = 4
// from cat
// treasure achievement = 5
// personal achievement = 6
// guild achievement = 7
RewardType uint8
Unk2 uint8 // Number of uint32s to read?
Unk3 []byte
AckHandle uint32
Unk0 uint8
// from gal
// daily = 0
// personal = 1
// personal rank = 2
// guild rank = 3
// gcp = 4
// from cat
// treasure achievement = 5
// personal achievement = 6
// guild achievement = 7
RewardType uint8
Unk2 uint8 // Number of uint32s to read?
Unk3 []byte
}
// Opcode returns the ID associated with this packet type.
@@ -32,18 +33,18 @@ func (m *MsgMhfAcquireUdItem) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAcquireUdItem) Parse(bf *byteframe.ByteFrame) error {
m.AckHandle = bf.ReadUint32()
func (m *MsgMhfAcquireUdItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8()
m.RewardType = bf.ReadUint8()
m.Unk2 = bf.ReadUint8()
for i := uint8(0); i < m.Unk2; i++ {
bf.ReadUint32()
}
m.Unk2 = bf.ReadUint8()
for i := uint8(0); i < m.Unk2; i++ {
bf.ReadUint32()
}
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAcquireUdItem) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAcquireUdItem) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -3,8 +3,9 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
)
// MsgMhfAddAchievement represents the MSG_MHF_ADD_ACHIEVEMENT
@@ -20,7 +21,7 @@ func (m *MsgMhfAddAchievement) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAddAchievement) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAddAchievement) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AchievementID = bf.ReadUint8()
m.Unk1 = bf.ReadUint16()
m.Unk2 = bf.ReadUint16()
@@ -28,6 +29,6 @@ func (m *MsgMhfAddAchievement) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAddAchievement) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAddAchievement) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -1,17 +1,18 @@
package mhfpacket
import (
"errors"
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/common/byteframe"
)
// MsgMhfAddGuildMissionCount represents the MSG_MHF_ADD_GUILD_MISSION_COUNT
type MsgMhfAddGuildMissionCount struct {
AckHandle uint32
MissionID uint32
Count uint32
AckHandle uint32
MissionID uint32
Count uint32
}
// Opcode returns the ID associated with this packet type.
@@ -20,14 +21,14 @@ func (m *MsgMhfAddGuildMissionCount) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAddGuildMissionCount) Parse(bf *byteframe.ByteFrame) error {
m.AckHandle = bf.ReadUint32()
m.MissionID = bf.ReadUint32()
m.Count = bf.ReadUint32()
return nil
func (m *MsgMhfAddGuildMissionCount) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.MissionID = bf.ReadUint32()
m.Count = bf.ReadUint32()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAddGuildMissionCount) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAddGuildMissionCount) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -1,16 +1,17 @@
package mhfpacket
import (
"errors"
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/common/byteframe"
)
// MsgMhfAddGuildWeeklyBonusExceptionalUser represents the MSG_MHF_ADD_GUILD_WEEKLY_BONUS_EXCEPTIONAL_USER
type MsgMhfAddGuildWeeklyBonusExceptionalUser struct {
AckHandle uint32
NumUsers uint8
AckHandle uint32
NumUsers uint8
}
// Opcode returns the ID associated with this packet type.
@@ -19,13 +20,13 @@ func (m *MsgMhfAddGuildWeeklyBonusExceptionalUser) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAddGuildWeeklyBonusExceptionalUser) Parse(bf *byteframe.ByteFrame) error {
m.AckHandle = bf.ReadUint32()
m.NumUsers = bf.ReadUint8()
return nil
func (m *MsgMhfAddGuildWeeklyBonusExceptionalUser) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.NumUsers = bf.ReadUint8()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAddGuildWeeklyBonusExceptionalUser) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAddGuildWeeklyBonusExceptionalUser) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -2,7 +2,8 @@ package mhfpacket
import (
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
"erupe-ce/common/byteframe"
)
// MsgMhfAddKouryouPoint represents the MSG_MHF_ADD_KOURYOU_POINT
@@ -17,14 +18,14 @@ func (m *MsgMhfAddKouryouPoint) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAddKouryouPoint) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAddKouryouPoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.KouryouPoints = bf.ReadUint32()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAddKouryouPoint) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAddKouryouPoint) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
bf.WriteUint32(m.AckHandle)
bf.WriteUint32(m.KouryouPoints)
return nil

View File

@@ -1,10 +1,11 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/common/byteframe"
)
// MsgMhfAddRewardSongCount represents the MSG_MHF_ADD_REWARD_SONG_COUNT
@@ -16,11 +17,11 @@ func (m *MsgMhfAddRewardSongCount) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAddRewardSongCount) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAddRewardSongCount) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAddRewardSongCount) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAddRewardSongCount) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -1,10 +1,11 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/common/byteframe"
)
// MsgMhfAddUdPoint represents the MSG_MHF_ADD_UD_POINT
@@ -20,7 +21,7 @@ func (m *MsgMhfAddUdPoint) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAddUdPoint) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAddUdPoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk1 = bf.ReadUint32()
m.Unk2 = bf.ReadUint32()
@@ -30,6 +31,6 @@ func (m *MsgMhfAddUdPoint) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAddUdPoint) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAddUdPoint) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -2,7 +2,8 @@ package mhfpacket
import (
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
"erupe-ce/common/byteframe"
)
// MsgMhfAddUdTacticsPoint represents the MSG_MHF_ADD_UD_TACTICS_POINT
@@ -18,7 +19,7 @@ func (m *MsgMhfAddUdTacticsPoint) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAddUdTacticsPoint) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAddUdTacticsPoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
m.Unk1 = bf.ReadUint32()
@@ -26,7 +27,7 @@ func (m *MsgMhfAddUdTacticsPoint) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAddUdTacticsPoint) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAddUdTacticsPoint) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
bf.WriteUint32(m.AckHandle)
bf.WriteUint16(m.Unk0)
bf.WriteUint32(m.Unk1)

View File

@@ -3,8 +3,9 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
)
// MsgMhfAnnounce represents the MSG_MHF_ANNOUNCE
@@ -22,7 +23,7 @@ func (m *MsgMhfAnnounce) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAnnounce) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAnnounce) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.IPAddress = bf.ReadUint32()
m.Port = bf.ReadUint16()
@@ -35,6 +36,6 @@ func (m *MsgMhfAnnounce) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAnnounce) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAnnounce) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -1,10 +1,11 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/common/byteframe"
)
// MsgMhfAnswerGuildScout represents the MSG_MHF_ANSWER_GUILD_SCOUT
@@ -20,7 +21,7 @@ func (m *MsgMhfAnswerGuildScout) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfAnswerGuildScout) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAnswerGuildScout) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.LeaderID = bf.ReadUint32()
m.Answer = bf.ReadBool()
@@ -28,6 +29,6 @@ func (m *MsgMhfAnswerGuildScout) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAnswerGuildScout) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfAnswerGuildScout) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -2,11 +2,12 @@ package mhfpacket
import (
"errors"
"erupe-ce/utils/bfutil"
"erupe-ce/utils/stringsupport"
"erupe-ce/common/bfutil"
"erupe-ce/common/stringsupport"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
)
// MsgMhfApplyBbsArticle represents the MSG_MHF_APPLY_BBS_ARTICLE
@@ -25,7 +26,7 @@ func (m *MsgMhfApplyBbsArticle) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfApplyBbsArticle) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfApplyBbsArticle) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadBytes(16)
@@ -36,6 +37,6 @@ func (m *MsgMhfApplyBbsArticle) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfApplyBbsArticle) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfApplyBbsArticle) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -2,8 +2,9 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
)
// MsgMhfApplyCampaign represents the MSG_MHF_APPLY_CAMPAIGN
@@ -20,7 +21,7 @@ func (m *MsgMhfApplyCampaign) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfApplyCampaign) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfApplyCampaign) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadUint16()
@@ -29,6 +30,6 @@ func (m *MsgMhfApplyCampaign) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfApplyCampaign) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfApplyCampaign) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -2,9 +2,10 @@ package mhfpacket
import (
"errors"
"erupe-ce/config"
"erupe-ce/common/byteframe"
_config "erupe-ce/config"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
)
// MsgMhfApplyDistItem represents the MSG_MHF_APPLY_DIST_ITEM
@@ -22,20 +23,20 @@ func (m *MsgMhfApplyDistItem) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfApplyDistItem) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfApplyDistItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.DistributionType = bf.ReadUint8()
m.DistributionID = bf.ReadUint32()
if config.GetConfig().ClientID >= config.G8 {
if _config.ErupeConfig.RealClientMode >= _config.G8 {
m.Unk2 = bf.ReadUint32()
}
if config.GetConfig().ClientID >= config.G10 {
if _config.ErupeConfig.RealClientMode >= _config.G10 {
m.Unk3 = bf.ReadUint32()
}
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfApplyDistItem) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfApplyDistItem) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -2,8 +2,9 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
)
// MsgMhfArrangeGuildMember represents the MSG_MHF_ARRANGE_GUILD_MEMBER
@@ -19,7 +20,7 @@ func (m *MsgMhfArrangeGuildMember) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfArrangeGuildMember) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfArrangeGuildMember) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.GuildID = bf.ReadUint32()
bf.ReadUint8() // Zeroed
@@ -34,6 +35,6 @@ func (m *MsgMhfArrangeGuildMember) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfArrangeGuildMember) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfArrangeGuildMember) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -1,16 +1,17 @@
package mhfpacket
import (
"errors"
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/common/byteframe"
)
// MsgMhfCancelGuildMissionTarget represents the MSG_MHF_CANCEL_GUILD_MISSION_TARGET
type MsgMhfCancelGuildMissionTarget struct {
AckHandle uint32
MissionID uint32
AckHandle uint32
MissionID uint32
}
// Opcode returns the ID associated with this packet type.
@@ -19,13 +20,13 @@ func (m *MsgMhfCancelGuildMissionTarget) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfCancelGuildMissionTarget) Parse(bf *byteframe.ByteFrame) error {
m.AckHandle = bf.ReadUint32()
m.MissionID = bf.ReadUint32()
return nil
func (m *MsgMhfCancelGuildMissionTarget) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.MissionID = bf.ReadUint32()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfCancelGuildMissionTarget) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfCancelGuildMissionTarget) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -1,10 +1,11 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/common/byteframe"
)
// MsgMhfCancelGuildScout represents the MSG_MHF_CANCEL_GUILD_SCOUT
@@ -19,13 +20,13 @@ func (m *MsgMhfCancelGuildScout) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfCancelGuildScout) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfCancelGuildScout) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.InvitationID = bf.ReadUint32()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfCancelGuildScout) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfCancelGuildScout) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -3,8 +3,9 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
"erupe-ce/network/clientctx"
)
// MsgMhfCaravanMyRank represents the MSG_MHF_CARAVAN_MY_RANK
@@ -21,7 +22,7 @@ func (m *MsgMhfCaravanMyRank) Opcode() network.PacketID {
}
// Parse parses the packet from binary
func (m *MsgMhfCaravanMyRank) Parse(bf *byteframe.ByteFrame) error {
func (m *MsgMhfCaravanMyRank) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadUint32()
@@ -30,6 +31,6 @@ func (m *MsgMhfCaravanMyRank) Parse(bf *byteframe.ByteFrame) error {
}
// Build builds a binary packet from the current data.
func (m *MsgMhfCaravanMyRank) Build(bf *byteframe.ByteFrame) error {
func (m *MsgMhfCaravanMyRank) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

Some files were not shown because too many files have changed in this diff Show More