mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-02-06 10:06:53 +01:00
Merge branch 'refs/heads/main' into feature/hunting-tournament
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,3 +8,4 @@ savedata/*/
|
|||||||
*.lnk
|
*.lnk
|
||||||
*.bat
|
*.bat
|
||||||
/docker/db-data
|
/docker/db-data
|
||||||
|
screenshots/*
|
||||||
15
AUTHORS.md
15
AUTHORS.md
@@ -1,14 +1,15 @@
|
|||||||
# List of AUTHORS who contributed over time to the Erupe project
|
# List of authors who contributed to Erupe
|
||||||
|
|
||||||
## Point of current development
|
## Point of current development
|
||||||
The project is currently developed under https://github.com/ZeruLight/Erupe
|
The project is currently developed under https://github.com/ZeruLight/Erupe
|
||||||
|
|
||||||
## History of development
|
## History of development
|
||||||
Development of this project dates back to 2019, and was developed under various umbrellas over time:
|
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)
|
* 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)
|
* 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)
|
* Community Edition, 2022 (https://github.com/xl3lackout/Erupe)
|
||||||
* Zerulight, 2022-2023 (https://github.com/ZeruLight/Erupe)
|
* sekaiwish Fork, 2022 (https://github.com/sekaiwish/Erupe)
|
||||||
|
* ZeruLight, 2022-2023 (https://github.com/ZeruLight/Erupe)
|
||||||
|
|
||||||
## Authorship of the code
|
## Authorship of the code
|
||||||
Authorship is assigned for each commit within the git history, which is stored in these git repos:
|
Authorship is assigned for each commit within the git history, which is stored in these git repos:
|
||||||
@@ -17,13 +18,13 @@ Authorship is assigned for each commit within the git history, which is stored i
|
|||||||
* https://github.com/ricochhet/Erupe-Legacy
|
* https://github.com/ricochhet/Erupe-Legacy
|
||||||
* https://github.com/xl3lackout/Erupe
|
* https://github.com/xl3lackout/Erupe
|
||||||
|
|
||||||
Note there is a divergence between Ellie42s branch and xl3lackout where history has been lost.
|
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 the Erupe pre-2022
|
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.
|
If somebody can provide information, please contact us, so that we can make this history available.
|
||||||
|
|
||||||
## Exceptions with third-party libraries
|
## Exceptions with third-party libraries
|
||||||
The third-party libraries have their own way of addressing authorship and the authorship of commits importing/updating
|
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.
|
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.
|
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.
|
||||||
12
README.md
12
README.md
@@ -1,6 +1,6 @@
|
|||||||
# Erupe
|
# Erupe
|
||||||
|
|
||||||
## Client Compatiblity
|
## Client Compatibility
|
||||||
### Platforms
|
### Platforms
|
||||||
- PC
|
- PC
|
||||||
- PlayStation 3
|
- PlayStation 3
|
||||||
@@ -34,15 +34,15 @@ If you want to modify or compile Erupe yourself, please read on.
|
|||||||
|
|
||||||
## Docker
|
## Docker
|
||||||
|
|
||||||
Please see the readme in [docker/README.md](./docker/README.md). At the moment this is only really good for quick installs and checking out development not for production.
|
Please see [docker/README.md](./docker/README.md). This is intended for quick installs and development, not for production.
|
||||||
|
|
||||||
## Schemas
|
## Schemas
|
||||||
|
|
||||||
We source control the following schemas:
|
We source control the following schemas:
|
||||||
- Initialisation Schemas: These initialise the application database to a clean install from a specific version.
|
- Initialization Schema: This initializes the application database to a specific version (9.1.0).
|
||||||
- Update Schemas: These are update files they should be ran in order of version to get to the latest schema.
|
- 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 ran from the lastest available update schema or initial schema. These eventually get condensed into `Update Schemas` and then deleted when updated to a new version.
|
- 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 allow servers to be able to roll their own shops, distributions gachas and scenarios set ups.
|
- 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.
|
Note: Patch schemas are subject to change! You should only be using them if you are following along with development.
|
||||||
|
|
||||||
|
|||||||
175
common/mhfitem/mhfitem.go
Normal file
175
common/mhfitem/mhfitem.go
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
package mhfitem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/common/token"
|
||||||
|
_config "erupe-ce/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MHFItem struct {
|
||||||
|
ItemID uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type MHFSigilEffect struct {
|
||||||
|
ID uint16
|
||||||
|
Level uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type MHFSigil struct {
|
||||||
|
Effects []MHFSigilEffect
|
||||||
|
Unk0 uint8
|
||||||
|
Unk1 uint8
|
||||||
|
Unk2 uint8
|
||||||
|
Unk3 uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type MHFEquipment struct {
|
||||||
|
WarehouseID uint32
|
||||||
|
ItemType uint8
|
||||||
|
Unk0 uint8
|
||||||
|
ItemID uint16
|
||||||
|
Level uint16
|
||||||
|
Decorations []MHFItem
|
||||||
|
Sigils []MHFSigil
|
||||||
|
Unk1 uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type MHFItemStack struct {
|
||||||
|
WarehouseID uint32
|
||||||
|
Item MHFItem
|
||||||
|
Quantity uint16
|
||||||
|
Unk0 uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadWarehouseItem(bf *byteframe.ByteFrame) MHFItemStack {
|
||||||
|
var item MHFItemStack
|
||||||
|
item.WarehouseID = bf.ReadUint32()
|
||||||
|
if item.WarehouseID == 0 {
|
||||||
|
item.WarehouseID = token.RNG.Uint32()
|
||||||
|
}
|
||||||
|
item.Item.ItemID = bf.ReadUint16()
|
||||||
|
item.Quantity = bf.ReadUint16()
|
||||||
|
item.Unk0 = bf.ReadUint32()
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
func DiffItemStacks(o []MHFItemStack, u []MHFItemStack) []MHFItemStack {
|
||||||
|
// o = old, u = update, f = final
|
||||||
|
var f []MHFItemStack
|
||||||
|
for _, uItem := range u {
|
||||||
|
exists := false
|
||||||
|
for i := range o {
|
||||||
|
if o[i].WarehouseID == uItem.WarehouseID {
|
||||||
|
exists = true
|
||||||
|
o[i].Quantity = uItem.Quantity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
uItem.WarehouseID = token.RNG.Uint32()
|
||||||
|
f = append(f, uItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, oItem := range o {
|
||||||
|
if oItem.Quantity > 0 {
|
||||||
|
f = append(f, oItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is MHFItemStack) ToBytes() []byte {
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
bf.WriteUint32(is.WarehouseID)
|
||||||
|
bf.WriteUint16(is.Item.ItemID)
|
||||||
|
bf.WriteUint16(is.Quantity)
|
||||||
|
bf.WriteUint32(is.Unk0)
|
||||||
|
return bf.Data()
|
||||||
|
}
|
||||||
|
|
||||||
|
func SerializeWarehouseItems(i []MHFItemStack) []byte {
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
bf.WriteUint16(uint16(len(i)))
|
||||||
|
bf.WriteUint16(0) // Unused
|
||||||
|
for _, j := range i {
|
||||||
|
bf.WriteBytes(j.ToBytes())
|
||||||
|
}
|
||||||
|
return bf.Data()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadWarehouseEquipment(bf *byteframe.ByteFrame) MHFEquipment {
|
||||||
|
var equipment MHFEquipment
|
||||||
|
equipment.Decorations = make([]MHFItem, 3)
|
||||||
|
equipment.Sigils = make([]MHFSigil, 3)
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
equipment.Sigils[i].Effects = make([]MHFSigilEffect, 3)
|
||||||
|
}
|
||||||
|
equipment.WarehouseID = bf.ReadUint32()
|
||||||
|
if equipment.WarehouseID == 0 {
|
||||||
|
equipment.WarehouseID = token.RNG.Uint32()
|
||||||
|
}
|
||||||
|
equipment.ItemType = bf.ReadUint8()
|
||||||
|
equipment.Unk0 = bf.ReadUint8()
|
||||||
|
equipment.ItemID = bf.ReadUint16()
|
||||||
|
equipment.Level = bf.ReadUint16()
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
equipment.Decorations[i].ItemID = bf.ReadUint16()
|
||||||
|
}
|
||||||
|
if _config.ErupeConfig.RealClientMode >= _config.G1 {
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
for j := 0; j < 3; j++ {
|
||||||
|
equipment.Sigils[i].Effects[j].ID = bf.ReadUint16()
|
||||||
|
}
|
||||||
|
for j := 0; j < 3; j++ {
|
||||||
|
equipment.Sigils[i].Effects[j].Level = bf.ReadUint16()
|
||||||
|
}
|
||||||
|
equipment.Sigils[i].Unk0 = bf.ReadUint8()
|
||||||
|
equipment.Sigils[i].Unk1 = bf.ReadUint8()
|
||||||
|
equipment.Sigils[i].Unk2 = bf.ReadUint8()
|
||||||
|
equipment.Sigils[i].Unk3 = bf.ReadUint8()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
|
||||||
|
equipment.Unk1 = bf.ReadUint16()
|
||||||
|
}
|
||||||
|
return equipment
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e MHFEquipment) ToBytes() []byte {
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
bf.WriteUint32(e.WarehouseID)
|
||||||
|
bf.WriteUint8(e.ItemType)
|
||||||
|
bf.WriteUint8(e.Unk0)
|
||||||
|
bf.WriteUint16(e.ItemID)
|
||||||
|
bf.WriteUint16(e.Level)
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
bf.WriteUint16(e.Decorations[i].ItemID)
|
||||||
|
}
|
||||||
|
if _config.ErupeConfig.RealClientMode >= _config.G1 {
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
for j := 0; j < 3; j++ {
|
||||||
|
bf.WriteUint16(e.Sigils[i].Effects[j].ID)
|
||||||
|
}
|
||||||
|
for j := 0; j < 3; j++ {
|
||||||
|
bf.WriteUint16(e.Sigils[i].Effects[j].Level)
|
||||||
|
}
|
||||||
|
bf.WriteUint8(e.Sigils[i].Unk0)
|
||||||
|
bf.WriteUint8(e.Sigils[i].Unk1)
|
||||||
|
bf.WriteUint8(e.Sigils[i].Unk2)
|
||||||
|
bf.WriteUint8(e.Sigils[i].Unk3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
|
||||||
|
bf.WriteUint16(e.Unk1)
|
||||||
|
}
|
||||||
|
return bf.Data()
|
||||||
|
}
|
||||||
|
|
||||||
|
func SerializeWarehouseEquipment(i []MHFEquipment) []byte {
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
bf.WriteUint16(uint16(len(i)))
|
||||||
|
bf.WriteUint16(0) // Unused
|
||||||
|
for _, j := range i {
|
||||||
|
bf.WriteBytes(j.ToBytes())
|
||||||
|
}
|
||||||
|
return bf.Data()
|
||||||
|
}
|
||||||
@@ -5,18 +5,19 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var RNG = NewRNG()
|
||||||
|
|
||||||
// Generate returns an alphanumeric token of specified length
|
// Generate returns an alphanumeric token of specified length
|
||||||
func Generate(length int) string {
|
func Generate(length int) string {
|
||||||
rng := RNG()
|
|
||||||
var chars = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
var chars = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||||
b := make([]rune, length)
|
b := make([]rune, length)
|
||||||
for i := range b {
|
for i := range b {
|
||||||
b[i] = chars[rng.Intn(len(chars))]
|
b[i] = chars[RNG.Intn(len(chars))]
|
||||||
}
|
}
|
||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RNG returns a new RNG generator
|
// NewRNG returns a new NewRNG generator
|
||||||
func RNG() *rand.Rand {
|
func NewRNG() *rand.Rand {
|
||||||
return rand.New(rand.NewSource(time.Now().UnixNano()))
|
return rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
}
|
}
|
||||||
|
|||||||
15
config.json
15
config.json
@@ -9,7 +9,13 @@
|
|||||||
],
|
],
|
||||||
"PatchServerManifest": "",
|
"PatchServerManifest": "",
|
||||||
"PatchServerFile": "",
|
"PatchServerFile": "",
|
||||||
"ScreenshotAPIURL": "",
|
"Screenshots":{
|
||||||
|
"Enabled":true,
|
||||||
|
"Host":"127.0.0.1",
|
||||||
|
"Port":8080,
|
||||||
|
"OutputDir":"screenshots",
|
||||||
|
"UploadQuality":100
|
||||||
|
},
|
||||||
"DeleteOnSaveCorruption": false,
|
"DeleteOnSaveCorruption": false,
|
||||||
"ClientMode": "ZZ",
|
"ClientMode": "ZZ",
|
||||||
"QuestCacheExpiry": 300,
|
"QuestCacheExpiry": 300,
|
||||||
@@ -46,7 +52,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"GameplayOptions": {
|
"GameplayOptions": {
|
||||||
"FeaturedWeapons": 1,
|
"MinFeatureWeapons": 0,
|
||||||
|
"MaxFeatureWeapons": 1,
|
||||||
"MaximumNP": 100000,
|
"MaximumNP": 100000,
|
||||||
"MaximumRP": 50000,
|
"MaximumRP": 50000,
|
||||||
"MaximumFP": 120000,
|
"MaximumFP": 120000,
|
||||||
@@ -188,8 +195,8 @@
|
|||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
"Port": 53312
|
"Port": 53312
|
||||||
},
|
},
|
||||||
"SignV2": {
|
"API": {
|
||||||
"Enabled": false,
|
"Enabled": true,
|
||||||
"Port": 8080,
|
"Port": 8080,
|
||||||
"PatchServer": "",
|
"PatchServer": "",
|
||||||
"Banners": [],
|
"Banners": [],
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ type Config struct {
|
|||||||
LoginNotices []string // MHFML string of the login notices displayed
|
LoginNotices []string // MHFML string of the login notices displayed
|
||||||
PatchServerManifest string // Manifest patch server override
|
PatchServerManifest string // Manifest patch server override
|
||||||
PatchServerFile string // File 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
|
DeleteOnSaveCorruption bool // Attempts to save corrupted data will flag the save for deletion
|
||||||
ClientMode string
|
ClientMode string
|
||||||
RealClientMode Mode
|
RealClientMode Mode
|
||||||
@@ -87,6 +86,8 @@ type Config struct {
|
|||||||
EarthID int32
|
EarthID int32
|
||||||
EarthMonsters []int32
|
EarthMonsters []int32
|
||||||
SaveDumps SaveDumpOptions
|
SaveDumps SaveDumpOptions
|
||||||
|
Screenshots ScreenshotsOptions
|
||||||
|
|
||||||
DebugOptions DebugOptions
|
DebugOptions DebugOptions
|
||||||
GameplayOptions GameplayOptions
|
GameplayOptions GameplayOptions
|
||||||
Discord Discord
|
Discord Discord
|
||||||
@@ -94,7 +95,7 @@ type Config struct {
|
|||||||
Courses []Course
|
Courses []Course
|
||||||
Database Database
|
Database Database
|
||||||
Sign Sign
|
Sign Sign
|
||||||
SignV2 SignV2
|
API API
|
||||||
Channel Channel
|
Channel Channel
|
||||||
Entrance Entrance
|
Entrance Entrance
|
||||||
}
|
}
|
||||||
@@ -105,6 +106,14 @@ type SaveDumpOptions struct {
|
|||||||
OutputDir string
|
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.
|
// DebugOptions holds various debug/temporary options for use while developing Erupe.
|
||||||
type DebugOptions struct {
|
type DebugOptions struct {
|
||||||
CleanDB bool // Automatically wipes the DB on server reset.
|
CleanDB bool // Automatically wipes the DB on server reset.
|
||||||
@@ -132,7 +141,8 @@ type CapLinkOptions struct {
|
|||||||
|
|
||||||
// GameplayOptions has various gameplay modifiers
|
// GameplayOptions has various gameplay modifiers
|
||||||
type GameplayOptions struct {
|
type GameplayOptions struct {
|
||||||
FeaturedWeapons int // Number of Active Feature weapons to generate daily
|
MinFeatureWeapons int // Minimum number of Active Feature weapons to generate daily
|
||||||
|
MaxFeatureWeapons int // Maximum number of Active Feature weapons to generate daily
|
||||||
MaximumNP int // Maximum number of NP held by a player
|
MaximumNP int // Maximum number of NP held by a player
|
||||||
MaximumRP uint16 // Maximum number of RP held by a player
|
MaximumRP uint16 // Maximum number of RP held by a player
|
||||||
MaximumFP uint32 // Maximum number of FP held by a player
|
MaximumFP uint32 // Maximum number of FP held by a player
|
||||||
@@ -227,29 +237,29 @@ type Sign struct {
|
|||||||
Port int
|
Port int
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignV2 holds the new sign server config
|
// API holds server config
|
||||||
type SignV2 struct {
|
type API struct {
|
||||||
Enabled bool
|
Enabled bool
|
||||||
Port int
|
Port int
|
||||||
PatchServer string
|
PatchServer string
|
||||||
Banners []SignV2Banner
|
Banners []APISignBanner
|
||||||
Messages []SignV2Message
|
Messages []APISignMessage
|
||||||
Links []SignV2Link
|
Links []APISignLink
|
||||||
}
|
}
|
||||||
|
|
||||||
type SignV2Banner struct {
|
type APISignBanner struct {
|
||||||
Src string `json:"src"` // Displayed image URL
|
Src string `json:"src"` // Displayed image URL
|
||||||
Link string `json:"link"` // Link accessed on click
|
Link string `json:"link"` // Link accessed on click
|
||||||
}
|
}
|
||||||
|
|
||||||
type SignV2Message struct {
|
type APISignMessage struct {
|
||||||
Message string `json:"message"` // Displayed message
|
Message string `json:"message"` // Displayed message
|
||||||
Date int64 `json:"date"` // Displayed date
|
Date int64 `json:"date"` // Displayed date
|
||||||
Kind int `json:"kind"` // 0 for 'Default', 1 for 'New'
|
Kind int `json:"kind"` // 0 for 'Default', 1 for 'New'
|
||||||
Link string `json:"link"` // Link accessed on click
|
Link string `json:"link"` // Link accessed on click
|
||||||
}
|
}
|
||||||
|
|
||||||
type SignV2Link struct {
|
type APISignLink struct {
|
||||||
Name string `json:"name"` // Displayed name
|
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.
|
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
|
Link string `json:"link"` // Link accessed on click
|
||||||
@@ -351,6 +361,10 @@ func LoadConfig() (*Config, error) {
|
|||||||
c.RealClientMode = ZZ
|
c.RealClientMode = ZZ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.GameplayOptions.MinFeatureWeapons > c.GameplayOptions.MaxFeatureWeapons {
|
||||||
|
c.GameplayOptions.MinFeatureWeapons = c.GameplayOptions.MaxFeatureWeapons
|
||||||
|
}
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: ../
|
context: ../
|
||||||
volumes:
|
volumes:
|
||||||
- ./config.json:/app/erupe/config.json
|
- ../config.json:/app/erupe/config.json
|
||||||
- ./bin:/app/erupe/bin
|
- ../bin:/app/erupe/bin
|
||||||
- ./savedata:/app/erupe/savedata
|
- ./savedata:/app/erupe/savedata
|
||||||
ports:
|
ports:
|
||||||
# (Make sure these match config.json)
|
# (Make sure these match config.json)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
echo "INIT!"
|
echo "INIT!"
|
||||||
pg_restore --username="$POSTGRES_USER" --dbname="$POSTGRES_DB" --verbose /schemas/initialisation-schema/9.1-init.sql
|
pg_restore --username="$POSTGRES_USER" --dbname="$POSTGRES_DB" --verbose /schemas/init.sql
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
22
main.go
22
main.go
@@ -10,11 +10,11 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"erupe-ce/server/api"
|
||||||
"erupe-ce/server/channelserver"
|
"erupe-ce/server/channelserver"
|
||||||
"erupe-ce/server/discordbot"
|
"erupe-ce/server/discordbot"
|
||||||
"erupe-ce/server/entranceserver"
|
"erupe-ce/server/entranceserver"
|
||||||
"erupe-ce/server/signserver"
|
"erupe-ce/server/signserver"
|
||||||
"erupe-ce/server/signv2server"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
@@ -181,21 +181,21 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New Sign server
|
// New Sign server
|
||||||
var newSignServer *signv2server.Server
|
var ApiServer *api.APIServer
|
||||||
if config.SignV2.Enabled {
|
if config.API.Enabled {
|
||||||
newSignServer = signv2server.NewServer(
|
ApiServer = api.NewAPIServer(
|
||||||
&signv2server.Config{
|
&api.Config{
|
||||||
Logger: logger.Named("sign"),
|
Logger: logger.Named("sign"),
|
||||||
ErupeConfig: _config.ErupeConfig,
|
ErupeConfig: _config.ErupeConfig,
|
||||||
DB: db,
|
DB: db,
|
||||||
})
|
})
|
||||||
err = newSignServer.Start()
|
err = ApiServer.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
preventClose(fmt.Sprintf("SignV2: Failed to start, %s", err.Error()))
|
preventClose(fmt.Sprintf("API: Failed to start, %s", err.Error()))
|
||||||
}
|
}
|
||||||
logger.Info("SignV2: Started successfully")
|
logger.Info("API: Started successfully")
|
||||||
} else {
|
} else {
|
||||||
logger.Info("SignV2: Disabled")
|
logger.Info("API: Disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
var channels []*channelserver.Server
|
var channels []*channelserver.Server
|
||||||
@@ -273,8 +273,8 @@ func main() {
|
|||||||
signServer.Shutdown()
|
signServer.Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.SignV2.Enabled {
|
if config.API.Enabled {
|
||||||
newSignServer.Shutdown()
|
ApiServer.Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Entrance.Enabled {
|
if config.Entrance.Enabled {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
// MsgMhfEnumerateWarehouse represents the MSG_MHF_ENUMERATE_WAREHOUSE
|
// MsgMhfEnumerateWarehouse represents the MSG_MHF_ENUMERATE_WAREHOUSE
|
||||||
type MsgMhfEnumerateWarehouse struct {
|
type MsgMhfEnumerateWarehouse struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
BoxType string
|
BoxType uint8
|
||||||
BoxIndex uint8
|
BoxIndex uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,15 +23,10 @@ func (m *MsgMhfEnumerateWarehouse) Opcode() network.PacketID {
|
|||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfEnumerateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfEnumerateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
boxType := bf.ReadUint8()
|
m.BoxType = bf.ReadUint8()
|
||||||
switch boxType {
|
|
||||||
case 0:
|
|
||||||
m.BoxType = "item"
|
|
||||||
case 1:
|
|
||||||
m.BoxType = "equip"
|
|
||||||
}
|
|
||||||
m.BoxIndex = bf.ReadUint8()
|
m.BoxIndex = bf.ReadUint8()
|
||||||
_ = bf.ReadUint16()
|
bf.ReadUint8() // Zeroed
|
||||||
|
bf.ReadUint8() // Zeroed
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
type MsgMhfOperateWarehouse struct {
|
type MsgMhfOperateWarehouse struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
Operation uint8
|
Operation uint8
|
||||||
BoxType string
|
BoxType uint8
|
||||||
BoxIndex uint8
|
BoxIndex uint8
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
@@ -27,17 +27,13 @@ func (m *MsgMhfOperateWarehouse) Opcode() network.PacketID {
|
|||||||
func (m *MsgMhfOperateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfOperateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.Operation = bf.ReadUint8()
|
m.Operation = bf.ReadUint8()
|
||||||
boxType := bf.ReadUint8()
|
m.BoxType = bf.ReadUint8()
|
||||||
switch boxType {
|
|
||||||
case 0:
|
|
||||||
m.BoxType = "item"
|
|
||||||
case 1:
|
|
||||||
m.BoxType = "equip"
|
|
||||||
}
|
|
||||||
m.BoxIndex = bf.ReadUint8()
|
m.BoxIndex = bf.ReadUint8()
|
||||||
_ = bf.ReadUint8() // lenName
|
lenName := bf.ReadUint8()
|
||||||
_ = bf.ReadUint16() // Unk
|
bf.ReadUint16() // Zeroed
|
||||||
|
if lenName > 0 {
|
||||||
m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
|
m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ func (m *MsgMhfStampcardStamp) Opcode() network.PacketID {
|
|||||||
func (m *MsgMhfStampcardStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfStampcardStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.HR = bf.ReadUint16()
|
m.HR = bf.ReadUint16()
|
||||||
|
if _config.ErupeConfig.RealClientMode >= _config.G1 {
|
||||||
m.GR = bf.ReadUint16()
|
m.GR = bf.ReadUint16()
|
||||||
|
}
|
||||||
m.Stamps = bf.ReadUint16()
|
m.Stamps = bf.ReadUint16()
|
||||||
bf.ReadUint16() // Zeroed
|
bf.ReadUint16() // Zeroed
|
||||||
if _config.ErupeConfig.RealClientMode > _config.Z1 {
|
if _config.ErupeConfig.RealClientMode > _config.Z1 {
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ func (m *MsgMhfUpdateGuacot) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clien
|
|||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.EntryCount = bf.ReadUint16()
|
m.EntryCount = bf.ReadUint16()
|
||||||
bf.ReadUint16() // Zeroed
|
bf.ReadUint16() // Zeroed
|
||||||
var temp Goocoo
|
|
||||||
for i := 0; i < int(m.EntryCount); i++ {
|
for i := 0; i < int(m.EntryCount); i++ {
|
||||||
|
var temp Goocoo
|
||||||
temp.Index = bf.ReadUint32()
|
temp.Index = bf.ReadUint32()
|
||||||
for j := 0; j < 22; j++ {
|
for j := 0; j < 22; j++ {
|
||||||
temp.Data1 = append(temp.Data1, bf.ReadInt16())
|
temp.Data1 = append(temp.Data1, bf.ReadInt16())
|
||||||
|
|||||||
@@ -2,24 +2,18 @@ package mhfpacket
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"erupe-ce/common/mhfitem"
|
||||||
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
"erupe-ce/network"
|
"erupe-ce/network"
|
||||||
"erupe-ce/network/clientctx"
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Item struct {
|
|
||||||
Unk0 uint32
|
|
||||||
ItemID uint16
|
|
||||||
Amount uint16
|
|
||||||
Unk1 uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// MsgMhfUpdateGuildItem represents the MSG_MHF_UPDATE_GUILD_ITEM
|
// MsgMhfUpdateGuildItem represents the MSG_MHF_UPDATE_GUILD_ITEM
|
||||||
type MsgMhfUpdateGuildItem struct {
|
type MsgMhfUpdateGuildItem struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
GuildID uint32
|
GuildID uint32
|
||||||
Items []Item
|
UpdatedItems []mhfitem.MHFItemStack
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
@@ -31,18 +25,12 @@ func (m *MsgMhfUpdateGuildItem) Opcode() network.PacketID {
|
|||||||
func (m *MsgMhfUpdateGuildItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfUpdateGuildItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.GuildID = bf.ReadUint32()
|
m.GuildID = bf.ReadUint32()
|
||||||
itemCount := int(bf.ReadUint16())
|
changes := int(bf.ReadUint16())
|
||||||
bf.ReadUint8() // Zeroed
|
bf.ReadUint8() // Zeroed
|
||||||
bf.ReadUint8() // Zeroed
|
bf.ReadUint8() // Zeroed
|
||||||
m.Items = make([]Item, itemCount)
|
for i := 0; i < changes; i++ {
|
||||||
|
m.UpdatedItems = append(m.UpdatedItems, mhfitem.ReadWarehouseItem(bf))
|
||||||
for i := 0; i < itemCount; i++ {
|
|
||||||
m.Items[i].Unk0 = bf.ReadUint32()
|
|
||||||
m.Items[i].ItemID = bf.ReadUint16()
|
|
||||||
m.Items[i].Amount = bf.ReadUint16()
|
|
||||||
m.Items[i].Unk1 = bf.ReadUint32()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package mhfpacket
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"erupe-ce/common/mhfitem"
|
||||||
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
"erupe-ce/network"
|
"erupe-ce/network"
|
||||||
@@ -11,7 +12,7 @@ import (
|
|||||||
// MsgMhfUpdateUnionItem represents the MSG_MHF_UPDATE_UNION_ITEM
|
// MsgMhfUpdateUnionItem represents the MSG_MHF_UPDATE_UNION_ITEM
|
||||||
type MsgMhfUpdateUnionItem struct {
|
type MsgMhfUpdateUnionItem struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
Items []Item
|
UpdatedItems []mhfitem.MHFItemStack
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
@@ -22,18 +23,12 @@ func (m *MsgMhfUpdateUnionItem) Opcode() network.PacketID {
|
|||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfUpdateUnionItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfUpdateUnionItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
itemCount := int(bf.ReadUint16())
|
changes := int(bf.ReadUint16())
|
||||||
bf.ReadUint8() // Zeroed
|
bf.ReadUint8() // Zeroed
|
||||||
bf.ReadUint8() // Zeroed
|
bf.ReadUint8() // Zeroed
|
||||||
m.Items = make([]Item, itemCount)
|
for i := 0; i < changes; i++ {
|
||||||
|
m.UpdatedItems = append(m.UpdatedItems, mhfitem.ReadWarehouseItem(bf))
|
||||||
for i := 0; i < itemCount; i++ {
|
|
||||||
m.Items[i].Unk0 = bf.ReadUint32()
|
|
||||||
m.Items[i].ItemID = bf.ReadUint16()
|
|
||||||
m.Items[i].Amount = bf.ReadUint16()
|
|
||||||
m.Items[i].Unk1 = bf.ReadUint32()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,25 +3,18 @@ package mhfpacket
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/common/mhfitem"
|
||||||
"erupe-ce/network"
|
"erupe-ce/network"
|
||||||
"erupe-ce/network/clientctx"
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WarehouseStack struct {
|
|
||||||
ID uint32
|
|
||||||
Index uint16
|
|
||||||
EquipType uint16
|
|
||||||
ItemID uint16
|
|
||||||
Quantity uint16
|
|
||||||
Data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// MsgMhfUpdateWarehouse represents the MSG_MHF_UPDATE_WAREHOUSE
|
// MsgMhfUpdateWarehouse represents the MSG_MHF_UPDATE_WAREHOUSE
|
||||||
type MsgMhfUpdateWarehouse struct {
|
type MsgMhfUpdateWarehouse struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
BoxType string
|
BoxType uint8
|
||||||
BoxIndex uint8
|
BoxIndex uint8
|
||||||
Updates []WarehouseStack
|
UpdatedItems []mhfitem.MHFItemStack
|
||||||
|
UpdatedEquipment []mhfitem.MHFEquipment
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
@@ -32,35 +25,19 @@ func (m *MsgMhfUpdateWarehouse) Opcode() network.PacketID {
|
|||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfUpdateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfUpdateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
boxType := bf.ReadUint8()
|
m.BoxType = bf.ReadUint8()
|
||||||
switch boxType {
|
|
||||||
case 0:
|
|
||||||
m.BoxType = "item"
|
|
||||||
case 1:
|
|
||||||
m.BoxType = "equip"
|
|
||||||
}
|
|
||||||
m.BoxIndex = bf.ReadUint8()
|
m.BoxIndex = bf.ReadUint8()
|
||||||
changes := int(bf.ReadUint16())
|
changes := int(bf.ReadUint16())
|
||||||
var stackUpdate WarehouseStack
|
bf.ReadUint8() // Zeroed
|
||||||
|
bf.ReadUint8() // Zeroed
|
||||||
for i := 0; i < changes; i++ {
|
for i := 0; i < changes; i++ {
|
||||||
switch boxType {
|
switch m.BoxType {
|
||||||
case 0:
|
case 0:
|
||||||
stackUpdate.ID = bf.ReadUint32()
|
m.UpdatedItems = append(m.UpdatedItems, mhfitem.ReadWarehouseItem(bf))
|
||||||
stackUpdate.Index = bf.ReadUint16()
|
|
||||||
stackUpdate.ItemID = bf.ReadUint16()
|
|
||||||
stackUpdate.Quantity = bf.ReadUint16()
|
|
||||||
_ = bf.ReadUint16() // Unk
|
|
||||||
m.Updates = append(m.Updates, stackUpdate)
|
|
||||||
case 1:
|
case 1:
|
||||||
stackUpdate.ID = bf.ReadUint32()
|
m.UpdatedEquipment = append(m.UpdatedEquipment, mhfitem.ReadWarehouseEquipment(bf))
|
||||||
stackUpdate.Index = bf.ReadUint16()
|
|
||||||
stackUpdate.EquipType = bf.ReadUint16()
|
|
||||||
stackUpdate.ItemID = bf.ReadUint16()
|
|
||||||
stackUpdate.Data = bf.ReadBytes(56)
|
|
||||||
m.Updates = append(m.Updates, stackUpdate)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = bf.ReadUint16()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,12 @@ BEGIN;
|
|||||||
|
|
||||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS psn_id TEXT;
|
ALTER TABLE users ADD COLUMN IF NOT EXISTS psn_id TEXT;
|
||||||
|
|
||||||
|
ALTER TABLE public.sign_sessions ADD COLUMN id SERIAL;
|
||||||
|
|
||||||
|
ALTER TABLE public.sign_sessions ADD CONSTRAINT sign_sessions_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
ALTER TABLE public.sign_sessions ALTER COLUMN user_id DROP NOT NULL;
|
||||||
|
|
||||||
|
ALTER TABLE public.sign_sessions ADD COLUMN psn_id TEXT;
|
||||||
|
|
||||||
END;
|
END;
|
||||||
6
schemas/patch-schema/20-reset-warehouses.sql
Normal file
6
schemas/patch-schema/20-reset-warehouses.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
UPDATE guilds SET item_box=NULL;
|
||||||
|
UPDATE users SET item_box=NULL;
|
||||||
|
|
||||||
|
END;
|
||||||
5
schemas/patch-schema/21-rename-hrp.sql
Normal file
5
schemas/patch-schema/21-rename-hrp.sql
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.characters RENAME hrp TO hr;
|
||||||
|
|
||||||
|
END;
|
||||||
6
schemas/patch-schema/22-clan-changing-room.sql
Normal file
6
schemas/patch-schema/22-clan-changing-room.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
ALTER TABLE guilds ADD COLUMN IF NOT EXISTS room_rp INT DEFAULT 0;
|
||||||
|
ALTER TABLE guilds ADD COLUMN IF NOT EXISTS room_expiry TIMESTAMP WITHOUT TIME ZONE;
|
||||||
|
|
||||||
|
END;
|
||||||
6
schemas/patch-schema/fix-weekly-stamps.sql
Normal file
6
schemas/patch-schema/fix-weekly-stamps.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.stamps RENAME hl_next TO hl_checked;
|
||||||
|
ALTER TABLE IF EXISTS public.stamps RENAME ex_next TO ex_checked;
|
||||||
|
|
||||||
|
END;
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
|
|
||||||
ALTER TABLE public.sign_sessions ADD COLUMN id SERIAL;
|
|
||||||
|
|
||||||
ALTER TABLE public.sign_sessions ADD CONSTRAINT sign_sessions_pkey PRIMARY KEY (id);
|
|
||||||
|
|
||||||
ALTER TABLE public.sign_sessions ALTER COLUMN user_id DROP NOT NULL;
|
|
||||||
|
|
||||||
ALTER TABLE public.sign_sessions ADD COLUMN psn_id TEXT;
|
|
||||||
|
|
||||||
END;
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package signv2server
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"erupe-ce/config"
|
_config "erupe-ce/config"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -21,8 +21,8 @@ type Config struct {
|
|||||||
ErupeConfig *_config.Config
|
ErupeConfig *_config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server is the MHF custom launcher sign server.
|
// APIServer is Erupes Standard API interface
|
||||||
type Server struct {
|
type APIServer struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
erupeConfig *_config.Config
|
erupeConfig *_config.Config
|
||||||
@@ -31,9 +31,9 @@ type Server struct {
|
|||||||
isShuttingDown bool
|
isShuttingDown bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a new Server type.
|
// NewAPIServer creates a new Server type.
|
||||||
func NewServer(config *Config) *Server {
|
func NewAPIServer(config *Config) *APIServer {
|
||||||
s := &Server{
|
s := &APIServer{
|
||||||
logger: config.Logger,
|
logger: config.Logger,
|
||||||
erupeConfig: config.ErupeConfig,
|
erupeConfig: config.ErupeConfig,
|
||||||
db: config.DB,
|
db: config.DB,
|
||||||
@@ -43,7 +43,7 @@ func NewServer(config *Config) *Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start starts the server in a new goroutine.
|
// Start starts the server in a new goroutine.
|
||||||
func (s *Server) Start() error {
|
func (s *APIServer) Start() error {
|
||||||
// Set up the routes responsible for serving the launcher HTML, serverlist, unique name check, and JP auth.
|
// Set up the routes responsible for serving the launcher HTML, serverlist, unique name check, and JP auth.
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
r.HandleFunc("/launcher", s.Launcher)
|
r.HandleFunc("/launcher", s.Launcher)
|
||||||
@@ -52,9 +52,11 @@ func (s *Server) Start() error {
|
|||||||
r.HandleFunc("/character/create", s.CreateCharacter)
|
r.HandleFunc("/character/create", s.CreateCharacter)
|
||||||
r.HandleFunc("/character/delete", s.DeleteCharacter)
|
r.HandleFunc("/character/delete", s.DeleteCharacter)
|
||||||
r.HandleFunc("/character/export", s.ExportSave)
|
r.HandleFunc("/character/export", s.ExportSave)
|
||||||
|
r.HandleFunc("/api/ss/bbs/upload.php", s.ScreenShot)
|
||||||
|
r.HandleFunc("/api/ss/bbs/{id}", s.ScreenShotGet)
|
||||||
handler := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}))(r)
|
handler := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}))(r)
|
||||||
s.httpServer.Handler = handlers.LoggingHandler(os.Stdout, handler)
|
s.httpServer.Handler = handlers.LoggingHandler(os.Stdout, handler)
|
||||||
s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.SignV2.Port)
|
s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.API.Port)
|
||||||
|
|
||||||
serveError := make(chan error, 1)
|
serveError := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
@@ -74,7 +76,7 @@ func (s *Server) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown exits the server gracefully.
|
// Shutdown exits the server gracefully.
|
||||||
func (s *Server) Shutdown() {
|
func (s *APIServer) Shutdown() {
|
||||||
s.logger.Debug("Shutting down")
|
s.logger.Debug("Shutting down")
|
||||||
|
|
||||||
s.Lock()
|
s.Lock()
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package signv2server
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) createNewUser(ctx context.Context, username string, password string) (uint32, uint32, error) {
|
func (s *APIServer) createNewUser(ctx context.Context, username string, password string) (uint32, uint32, error) {
|
||||||
// Create salted hash of user password
|
// Create salted hash of user password
|
||||||
passwordHash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
passwordHash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -32,7 +32,7 @@ func (s *Server) createNewUser(ctx context.Context, username string, password st
|
|||||||
return id, rights, err
|
return id, rights, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) createLoginToken(ctx context.Context, uid uint32) (uint32, string, error) {
|
func (s *APIServer) createLoginToken(ctx context.Context, uid uint32) (uint32, string, error) {
|
||||||
loginToken := token.Generate(16)
|
loginToken := token.Generate(16)
|
||||||
var tid uint32
|
var tid uint32
|
||||||
err := s.db.QueryRowContext(ctx, "INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2) RETURNING id", uid, loginToken).Scan(&tid)
|
err := s.db.QueryRowContext(ctx, "INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2) RETURNING id", uid, loginToken).Scan(&tid)
|
||||||
@@ -42,7 +42,7 @@ func (s *Server) createLoginToken(ctx context.Context, uid uint32) (uint32, stri
|
|||||||
return tid, loginToken, nil
|
return tid, loginToken, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) userIDFromToken(ctx context.Context, token string) (uint32, error) {
|
func (s *APIServer) userIDFromToken(ctx context.Context, token string) (uint32, error) {
|
||||||
var userID uint32
|
var userID uint32
|
||||||
err := s.db.QueryRowContext(ctx, "SELECT user_id FROM sign_sessions WHERE token = $1", token).Scan(&userID)
|
err := s.db.QueryRowContext(ctx, "SELECT user_id FROM sign_sessions WHERE token = $1", token).Scan(&userID)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
@@ -53,10 +53,10 @@ func (s *Server) userIDFromToken(ctx context.Context, token string) (uint32, err
|
|||||||
return userID, nil
|
return userID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) createCharacter(ctx context.Context, userID uint32) (Character, error) {
|
func (s *APIServer) createCharacter(ctx context.Context, userID uint32) (Character, error) {
|
||||||
var character Character
|
var character Character
|
||||||
err := s.db.GetContext(ctx, &character,
|
err := s.db.GetContext(ctx, &character,
|
||||||
"SELECT id, name, is_female, weapon_type, hrp, gr, last_login FROM characters WHERE is_new_character = true AND user_id = $1 LIMIT 1",
|
"SELECT id, name, is_female, weapon_type, hr, gr, last_login FROM characters WHERE is_new_character = true AND user_id = $1 LIMIT 1",
|
||||||
userID,
|
userID,
|
||||||
)
|
)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
@@ -68,17 +68,17 @@ func (s *Server) createCharacter(ctx context.Context, userID uint32) (Character,
|
|||||||
err = s.db.GetContext(ctx, &character, `
|
err = s.db.GetContext(ctx, &character, `
|
||||||
INSERT INTO characters (
|
INSERT INTO characters (
|
||||||
user_id, is_female, is_new_character, name, unk_desc_string,
|
user_id, is_female, is_new_character, name, unk_desc_string,
|
||||||
hrp, gr, weapon_type, last_login
|
hr, gr, weapon_type, last_login
|
||||||
)
|
)
|
||||||
VALUES ($1, false, true, '', '', 0, 0, 0, $2)
|
VALUES ($1, false, true, '', '', 0, 0, 0, $2)
|
||||||
RETURNING id, name, is_female, weapon_type, hrp, gr, last_login`,
|
RETURNING id, name, is_female, weapon_type, hr, gr, last_login`,
|
||||||
userID, uint32(time.Now().Unix()),
|
userID, uint32(time.Now().Unix()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return character, err
|
return character, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) deleteCharacter(ctx context.Context, userID uint32, charID uint32) error {
|
func (s *APIServer) deleteCharacter(ctx context.Context, userID uint32, charID uint32) error {
|
||||||
var isNew bool
|
var isNew bool
|
||||||
err := s.db.QueryRow("SELECT is_new_character FROM characters WHERE id = $1", charID).Scan(&isNew)
|
err := s.db.QueryRow("SELECT is_new_character FROM characters WHERE id = $1", charID).Scan(&isNew)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -92,11 +92,11 @@ func (s *Server) deleteCharacter(ctx context.Context, userID uint32, charID uint
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) getCharactersForUser(ctx context.Context, uid uint32) ([]Character, error) {
|
func (s *APIServer) getCharactersForUser(ctx context.Context, uid uint32) ([]Character, error) {
|
||||||
var characters []Character
|
var characters []Character
|
||||||
err := s.db.SelectContext(
|
err := s.db.SelectContext(
|
||||||
ctx, &characters, `
|
ctx, &characters, `
|
||||||
SELECT id, name, is_female, weapon_type, hrp, gr, last_login
|
SELECT id, name, is_female, weapon_type, hr, gr, last_login
|
||||||
FROM characters
|
FROM characters
|
||||||
WHERE user_id = $1 AND deleted = false AND is_new_character = false ORDER BY id ASC`,
|
WHERE user_id = $1 AND deleted = false AND is_new_character = false ORDER BY id ASC`,
|
||||||
uid,
|
uid,
|
||||||
@@ -107,7 +107,7 @@ func (s *Server) getCharactersForUser(ctx context.Context, uid uint32) ([]Charac
|
|||||||
return characters, nil
|
return characters, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) getReturnExpiry(uid uint32) time.Time {
|
func (s *APIServer) getReturnExpiry(uid uint32) time.Time {
|
||||||
var returnExpiry, lastLogin time.Time
|
var returnExpiry, lastLogin time.Time
|
||||||
s.db.Get(&lastLogin, "SELECT COALESCE(last_login, now()) FROM users WHERE id=$1", uid)
|
s.db.Get(&lastLogin, "SELECT COALESCE(last_login, now()) FROM users WHERE id=$1", uid)
|
||||||
if time.Now().Add((time.Hour * 24) * -90).After(lastLogin) {
|
if time.Now().Add((time.Hour * 24) * -90).After(lastLogin) {
|
||||||
@@ -124,7 +124,7 @@ func (s *Server) getReturnExpiry(uid uint32) time.Time {
|
|||||||
return returnExpiry
|
return returnExpiry
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) exportSave(ctx context.Context, uid uint32, cid uint32) (map[string]interface{}, error) {
|
func (s *APIServer) exportSave(ctx context.Context, uid uint32, cid uint32) (map[string]interface{}, error) {
|
||||||
row := s.db.QueryRowxContext(ctx, "SELECT * FROM characters WHERE id=$1 AND user_id=$2", cid, uid)
|
row := s.db.QueryRowxContext(ctx, "SELECT * FROM characters WHERE id=$1 AND user_id=$2", cid, uid)
|
||||||
result := make(map[string]interface{})
|
result := make(map[string]interface{})
|
||||||
err := row.MapScan(result)
|
err := row.MapScan(result)
|
||||||
@@ -1,15 +1,24 @@
|
|||||||
package signv2server
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
"errors"
|
"errors"
|
||||||
_config "erupe-ce/config"
|
_config "erupe-ce/config"
|
||||||
"erupe-ce/server/channelserver"
|
"erupe-ce/server/channelserver"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/jpeg"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
@@ -21,9 +30,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type LauncherResponse struct {
|
type LauncherResponse struct {
|
||||||
Banners []_config.SignV2Banner `json:"banners"`
|
Banners []_config.APISignBanner `json:"banners"`
|
||||||
Messages []_config.SignV2Message `json:"messages"`
|
Messages []_config.APISignMessage `json:"messages"`
|
||||||
Links []_config.SignV2Link `json:"links"`
|
Links []_config.APISignLink `json:"links"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
@@ -37,7 +46,7 @@ type Character struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
IsFemale bool `json:"isFemale" db:"is_female"`
|
IsFemale bool `json:"isFemale" db:"is_female"`
|
||||||
Weapon uint32 `json:"weapon" db:"weapon_type"`
|
Weapon uint32 `json:"weapon" db:"weapon_type"`
|
||||||
HR uint32 `json:"hr" db:"hrp"`
|
HR uint32 `json:"hr" db:"hr"`
|
||||||
GR uint32 `json:"gr"`
|
GR uint32 `json:"gr"`
|
||||||
LastLogin int32 `json:"lastLogin" db:"last_login"`
|
LastLogin int32 `json:"lastLogin" db:"last_login"`
|
||||||
}
|
}
|
||||||
@@ -66,7 +75,7 @@ type ExportData struct {
|
|||||||
Character map[string]interface{} `json:"character"`
|
Character map[string]interface{} `json:"character"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) newAuthData(userID uint32, userRights uint32, userTokenID uint32, userToken string, characters []Character) AuthData {
|
func (s *APIServer) newAuthData(userID uint32, userRights uint32, userTokenID uint32, userToken string, characters []Character) AuthData {
|
||||||
resp := AuthData{
|
resp := AuthData{
|
||||||
CurrentTS: uint32(channelserver.TimeAdjusted().Unix()),
|
CurrentTS: uint32(channelserver.TimeAdjusted().Unix()),
|
||||||
ExpiryTS: uint32(s.getReturnExpiry(userID).Unix()),
|
ExpiryTS: uint32(s.getReturnExpiry(userID).Unix()),
|
||||||
@@ -77,7 +86,7 @@ func (s *Server) newAuthData(userID uint32, userRights uint32, userTokenID uint3
|
|||||||
Token: userToken,
|
Token: userToken,
|
||||||
},
|
},
|
||||||
Characters: characters,
|
Characters: characters,
|
||||||
PatchServer: s.erupeConfig.SignV2.PatchServer,
|
PatchServer: s.erupeConfig.API.PatchServer,
|
||||||
Notices: []string{},
|
Notices: []string{},
|
||||||
}
|
}
|
||||||
if s.erupeConfig.DebugOptions.MaxLauncherHR {
|
if s.erupeConfig.DebugOptions.MaxLauncherHR {
|
||||||
@@ -103,16 +112,16 @@ func (s *Server) newAuthData(userID uint32, userRights uint32, userTokenID uint3
|
|||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Launcher(w http.ResponseWriter, r *http.Request) {
|
func (s *APIServer) Launcher(w http.ResponseWriter, r *http.Request) {
|
||||||
var respData LauncherResponse
|
var respData LauncherResponse
|
||||||
respData.Banners = s.erupeConfig.SignV2.Banners
|
respData.Banners = s.erupeConfig.API.Banners
|
||||||
respData.Messages = s.erupeConfig.SignV2.Messages
|
respData.Messages = s.erupeConfig.API.Messages
|
||||||
respData.Links = s.erupeConfig.SignV2.Links
|
respData.Links = s.erupeConfig.API.Links
|
||||||
w.Header().Add("Content-Type", "application/json")
|
w.Header().Add("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(respData)
|
json.NewEncoder(w).Encode(respData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Login(w http.ResponseWriter, r *http.Request) {
|
func (s *APIServer) Login(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
var reqData struct {
|
var reqData struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
@@ -164,7 +173,7 @@ func (s *Server) Login(w http.ResponseWriter, r *http.Request) {
|
|||||||
json.NewEncoder(w).Encode(respData)
|
json.NewEncoder(w).Encode(respData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Register(w http.ResponseWriter, r *http.Request) {
|
func (s *APIServer) Register(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
var reqData struct {
|
var reqData struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
@@ -204,7 +213,7 @@ func (s *Server) Register(w http.ResponseWriter, r *http.Request) {
|
|||||||
json.NewEncoder(w).Encode(respData)
|
json.NewEncoder(w).Encode(respData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) CreateCharacter(w http.ResponseWriter, r *http.Request) {
|
func (s *APIServer) CreateCharacter(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
var reqData struct {
|
var reqData struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
@@ -233,7 +242,7 @@ func (s *Server) CreateCharacter(w http.ResponseWriter, r *http.Request) {
|
|||||||
json.NewEncoder(w).Encode(character)
|
json.NewEncoder(w).Encode(character)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) DeleteCharacter(w http.ResponseWriter, r *http.Request) {
|
func (s *APIServer) DeleteCharacter(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
var reqData struct {
|
var reqData struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
@@ -258,7 +267,7 @@ func (s *Server) DeleteCharacter(w http.ResponseWriter, r *http.Request) {
|
|||||||
json.NewEncoder(w).Encode(struct{}{})
|
json.NewEncoder(w).Encode(struct{}{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ExportSave(w http.ResponseWriter, r *http.Request) {
|
func (s *APIServer) ExportSave(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
var reqData struct {
|
var reqData struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
@@ -286,3 +295,118 @@ func (s *Server) ExportSave(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Header().Add("Content-Type", "application/json")
|
w.Header().Add("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(save)
|
json.NewEncoder(w).Encode(save)
|
||||||
}
|
}
|
||||||
|
func (s *APIServer) ScreenShotGet(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Get the 'id' parameter from the URL
|
||||||
|
token := mux.Vars(r)["id"]
|
||||||
|
var tokenPattern = regexp.MustCompile(`[A-Za-z0-9]+`)
|
||||||
|
|
||||||
|
if !tokenPattern.MatchString(token) || token == "" {
|
||||||
|
http.Error(w, "Not Valid Token", http.StatusBadRequest)
|
||||||
|
|
||||||
|
}
|
||||||
|
// Open the image file
|
||||||
|
safePath := s.erupeConfig.Screenshots.OutputDir
|
||||||
|
path := filepath.Join(safePath, fmt.Sprintf("%s.jpg", token))
|
||||||
|
result, err := verifyPath(path, safePath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error " + err.Error())
|
||||||
|
} else {
|
||||||
|
fmt.Println("Canonical: " + result)
|
||||||
|
|
||||||
|
file, err := os.Open(result)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Image not found", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
// Set content type header to image/jpeg
|
||||||
|
w.Header().Set("Content-Type", "image/jpeg")
|
||||||
|
// Copy the image content to the response writer
|
||||||
|
if _, err := io.Copy(w, file); err != nil {
|
||||||
|
http.Error(w, "Unable to send image", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (s *APIServer) ScreenShot(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Create a struct representing the XML result
|
||||||
|
type Result struct {
|
||||||
|
XMLName xml.Name `xml:"result"`
|
||||||
|
Code string `xml:"code"`
|
||||||
|
}
|
||||||
|
// Set the Content-Type header to specify that the response is in XML format
|
||||||
|
w.Header().Set("Content-Type", "text/xml")
|
||||||
|
result := Result{Code: "200"}
|
||||||
|
if !s.erupeConfig.Screenshots.Enabled {
|
||||||
|
result = Result{Code: "400"}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
result = Result{Code: "405"}
|
||||||
|
}
|
||||||
|
// Get File from Request
|
||||||
|
file, _, err := r.FormFile("img")
|
||||||
|
if err != nil {
|
||||||
|
result = Result{Code: "400"}
|
||||||
|
}
|
||||||
|
var tokenPattern = regexp.MustCompile(`[A-Za-z0-9]+`)
|
||||||
|
token := r.FormValue("token")
|
||||||
|
if !tokenPattern.MatchString(token) || token == "" {
|
||||||
|
result = Result{Code: "401"}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate file
|
||||||
|
img, _, err := image.Decode(file)
|
||||||
|
if err != nil {
|
||||||
|
result = Result{Code: "400"}
|
||||||
|
}
|
||||||
|
|
||||||
|
safePath := s.erupeConfig.Screenshots.OutputDir
|
||||||
|
|
||||||
|
path := filepath.Join(safePath, fmt.Sprintf("%s.jpg", token))
|
||||||
|
verified, err := verifyPath(path, safePath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
result = Result{Code: "500"}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
_, err = os.Stat(safePath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
err = os.MkdirAll(safePath, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Error writing screenshot, could not create folder")
|
||||||
|
result = Result{Code: "500"}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.logger.Error("Error writing screenshot")
|
||||||
|
result = Result{Code: "500"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create or open the output file
|
||||||
|
outputFile, err := os.Create(verified)
|
||||||
|
if err != nil {
|
||||||
|
result = Result{Code: "500"}
|
||||||
|
}
|
||||||
|
defer outputFile.Close()
|
||||||
|
|
||||||
|
// Encode the image and write it to the file
|
||||||
|
err = jpeg.Encode(outputFile, img, &jpeg.Options{Quality: s.erupeConfig.Screenshots.UploadQuality})
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Error writing screenshot, could not write file", zap.Error(err))
|
||||||
|
result = Result{Code: "500"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Marshal the struct into XML
|
||||||
|
xmlData, err := xml.Marshal(result)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Unable to marshal XML", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Write the XML response with a 200 status code
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write(xmlData)
|
||||||
|
}
|
||||||
37
server/api/utils.go
Normal file
37
server/api/utils.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func inTrustedRoot(path string, trustedRoot string) error {
|
||||||
|
for path != "/" {
|
||||||
|
path = filepath.Dir(path)
|
||||||
|
if path == trustedRoot {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors.New("path is outside of trusted root")
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyPath(path string, trustedRoot string) (string, error) {
|
||||||
|
|
||||||
|
c := filepath.Clean(path)
|
||||||
|
fmt.Println("Cleaned path: " + c)
|
||||||
|
|
||||||
|
r, err := filepath.EvalSymlinks(c)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error " + err.Error())
|
||||||
|
return c, errors.New("Unsafe or invalid path specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = inTrustedRoot(r, trustedRoot)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error " + err.Error())
|
||||||
|
return r, errors.New("Unsafe or invalid path specified")
|
||||||
|
} else {
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package channelserver
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"erupe-ce/common/mhfcourse"
|
"erupe-ce/common/mhfcourse"
|
||||||
|
"erupe-ce/common/mhfitem"
|
||||||
"erupe-ce/common/mhfmon"
|
"erupe-ce/common/mhfmon"
|
||||||
ps "erupe-ce/common/pascalstring"
|
ps "erupe-ce/common/pascalstring"
|
||||||
"erupe-ce/common/stringsupport"
|
"erupe-ce/common/stringsupport"
|
||||||
@@ -812,93 +813,33 @@ func handleMsgMhfEnumeratePrice(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
func handleMsgMhfGetExtraInfo(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfGetExtraInfo(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
|
func userGetItems(s *Session) []mhfitem.MHFItemStack {
|
||||||
|
var data []byte
|
||||||
|
var items []mhfitem.MHFItemStack
|
||||||
|
s.server.db.QueryRow(`SELECT item_box FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&data)
|
||||||
|
if len(data) > 0 {
|
||||||
|
box := byteframe.NewByteFrameFromBytes(data)
|
||||||
|
numStacks := box.ReadUint16()
|
||||||
|
box.ReadUint16() // Unused
|
||||||
|
for i := 0; i < int(numStacks); i++ {
|
||||||
|
items = append(items, mhfitem.ReadWarehouseItem(box))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfEnumerateUnionItem(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfEnumerateUnionItem(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfEnumerateUnionItem)
|
pkt := p.(*mhfpacket.MsgMhfEnumerateUnionItem)
|
||||||
var boxContents []byte
|
items := userGetItems(s)
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
err := s.server.db.QueryRow("SELECT item_box FROM users, characters WHERE characters.id = $1 AND users.id = characters.user_id", int(s.charID)).Scan(&boxContents)
|
bf.WriteBytes(mhfitem.SerializeWarehouseItems(items))
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to get shared item box contents from db", zap.Error(err))
|
|
||||||
bf.WriteBytes(make([]byte, 4))
|
|
||||||
} else {
|
|
||||||
if len(boxContents) == 0 {
|
|
||||||
bf.WriteBytes(make([]byte, 4))
|
|
||||||
} else {
|
|
||||||
amount := len(boxContents) / 4
|
|
||||||
bf.WriteUint16(uint16(amount))
|
|
||||||
bf.WriteUint32(0x00)
|
|
||||||
bf.WriteUint16(0x00)
|
|
||||||
for i := 0; i < amount; i++ {
|
|
||||||
bf.WriteUint32(binary.BigEndian.Uint32(boxContents[i*4 : i*4+4]))
|
|
||||||
if i+1 != amount {
|
|
||||||
bf.WriteUint64(0x00)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfUpdateUnionItem(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfUpdateUnionItem(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfUpdateUnionItem)
|
pkt := p.(*mhfpacket.MsgMhfUpdateUnionItem)
|
||||||
// Get item cache from DB
|
newStacks := mhfitem.DiffItemStacks(userGetItems(s), pkt.UpdatedItems)
|
||||||
var boxContents []byte
|
s.server.db.Exec(`UPDATE users u SET item_box=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, mhfitem.SerializeWarehouseItems(newStacks), s.charID)
|
||||||
var oldItems []Item
|
|
||||||
|
|
||||||
err := s.server.db.QueryRow("SELECT item_box FROM users, characters WHERE characters.id = $1 AND users.id = characters.user_id", int(s.charID)).Scan(&boxContents)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to get shared item box contents from db", zap.Error(err))
|
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
amount := len(boxContents) / 4
|
|
||||||
oldItems = make([]Item, amount)
|
|
||||||
for i := 0; i < amount; i++ {
|
|
||||||
oldItems[i].ItemId = binary.BigEndian.Uint16(boxContents[i*4 : i*4+2])
|
|
||||||
oldItems[i].Amount = binary.BigEndian.Uint16(boxContents[i*4+2 : i*4+4])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update item stacks
|
|
||||||
newItems := make([]Item, len(oldItems))
|
|
||||||
copy(newItems, oldItems)
|
|
||||||
for i := 0; i < len(pkt.Items); i++ {
|
|
||||||
for j := 0; j <= len(oldItems); j++ {
|
|
||||||
if j == len(oldItems) {
|
|
||||||
var newItem Item
|
|
||||||
newItem.ItemId = pkt.Items[i].ItemID
|
|
||||||
newItem.Amount = pkt.Items[i].Amount
|
|
||||||
newItems = append(newItems, newItem)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if pkt.Items[i].ItemID == oldItems[j].ItemId {
|
|
||||||
newItems[j].Amount = pkt.Items[i].Amount
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete empty item stacks
|
|
||||||
for i := len(newItems) - 1; i >= 0; i-- {
|
|
||||||
if int(newItems[i].Amount) == 0 {
|
|
||||||
copy(newItems[i:], newItems[i+1:])
|
|
||||||
newItems[len(newItems)-1] = make([]Item, 1)[0]
|
|
||||||
newItems = newItems[:len(newItems)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new item cache
|
|
||||||
bf := byteframe.NewByteFrame()
|
|
||||||
for i := 0; i < len(newItems); i++ {
|
|
||||||
bf.WriteUint16(newItems[i].ItemId)
|
|
||||||
bf.WriteUint16(newItems[i].Amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload new item cache
|
|
||||||
_, err = s.server.db.Exec("UPDATE users SET item_box = $1 FROM characters WHERE users.id = characters.user_id AND characters.id = $2", bf.Data(), int(s.charID))
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to update shared item box contents in db", zap.Error(err))
|
|
||||||
}
|
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -906,50 +847,54 @@ func handleMsgMhfGetCogInfo(s *Session, p mhfpacket.MHFPacket) {}
|
|||||||
|
|
||||||
func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfCheckWeeklyStamp)
|
pkt := p.(*mhfpacket.MsgMhfCheckWeeklyStamp)
|
||||||
weekCurrentStart := TimeWeekStart()
|
|
||||||
weekNextStart := TimeWeekNext()
|
|
||||||
var total, redeemed, updated uint16
|
var total, redeemed, updated uint16
|
||||||
var nextClaim time.Time
|
var lastCheck time.Time
|
||||||
err := s.server.db.QueryRow(fmt.Sprintf("SELECT %s_next FROM stamps WHERE character_id=$1", pkt.StampType), s.charID).Scan(&nextClaim)
|
err := s.server.db.QueryRow(fmt.Sprintf("SELECT %s_checked FROM stamps WHERE character_id=$1", pkt.StampType), s.charID).Scan(&lastCheck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.server.db.Exec("INSERT INTO stamps (character_id, hl_next, ex_next) VALUES ($1, $2, $2)", s.charID, weekNextStart)
|
lastCheck = TimeAdjusted()
|
||||||
nextClaim = weekNextStart
|
s.server.db.Exec("INSERT INTO stamps (character_id, hl_checked, ex_checked) VALUES ($1, $2, $2)", s.charID, TimeAdjusted())
|
||||||
|
} else {
|
||||||
|
s.server.db.Exec(fmt.Sprintf(`UPDATE stamps SET %s_checked=$1 WHERE character_id=$2`, pkt.StampType), TimeAdjusted(), s.charID)
|
||||||
}
|
}
|
||||||
if nextClaim.Before(weekCurrentStart) {
|
|
||||||
s.server.db.Exec(fmt.Sprintf("UPDATE stamps SET %s_total=%s_total+1, %s_next=$1 WHERE character_id=$2", pkt.StampType, pkt.StampType, pkt.StampType), weekNextStart, s.charID)
|
if lastCheck.Before(TimeWeekStart()) {
|
||||||
|
s.server.db.Exec(fmt.Sprintf("UPDATE stamps SET %s_total=%s_total+1 WHERE character_id=$1", pkt.StampType, pkt.StampType), s.charID)
|
||||||
updated = 1
|
updated = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
s.server.db.QueryRow(fmt.Sprintf("SELECT %s_total, %s_redeemed FROM stamps WHERE character_id=$1", pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed)
|
s.server.db.QueryRow(fmt.Sprintf("SELECT %s_total, %s_redeemed FROM stamps WHERE character_id=$1", pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed)
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
bf.WriteUint16(total)
|
bf.WriteUint16(total)
|
||||||
bf.WriteUint16(redeemed)
|
bf.WriteUint16(redeemed)
|
||||||
bf.WriteUint16(updated)
|
bf.WriteUint16(updated)
|
||||||
bf.WriteUint32(0) // Unk
|
bf.WriteUint16(0)
|
||||||
bf.WriteUint32(uint32(weekCurrentStart.Unix()))
|
bf.WriteUint16(0)
|
||||||
|
bf.WriteUint32(uint32(TimeWeekStart().Unix()))
|
||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfExchangeWeeklyStamp)
|
pkt := p.(*mhfpacket.MsgMhfExchangeWeeklyStamp)
|
||||||
var total, redeemed uint16
|
var total, redeemed uint16
|
||||||
var tktStack mhfpacket.WarehouseStack
|
var tktStack mhfitem.MHFItemStack
|
||||||
if pkt.Unk1 == 0xA { // Yearly Sub Ex
|
if pkt.Unk1 == 10 { // Yearly Sub Ex
|
||||||
s.server.db.QueryRow("UPDATE stamps SET hl_total=hl_total-48, hl_redeemed=hl_redeemed-48 WHERE character_id=$1 RETURNING hl_total, hl_redeemed", s.charID).Scan(&total, &redeemed)
|
s.server.db.QueryRow("UPDATE stamps SET hl_total=hl_total-48, hl_redeemed=hl_redeemed-48 WHERE character_id=$1 RETURNING hl_total, hl_redeemed", s.charID).Scan(&total, &redeemed)
|
||||||
tktStack = mhfpacket.WarehouseStack{ItemID: 0x08A2, Quantity: 1}
|
tktStack = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 2210}, Quantity: 1}
|
||||||
} else {
|
} else {
|
||||||
s.server.db.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed)
|
s.server.db.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed)
|
||||||
if pkt.StampType == "hl" {
|
if pkt.StampType == "hl" {
|
||||||
tktStack = mhfpacket.WarehouseStack{ItemID: 0x065E, Quantity: 5}
|
tktStack = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 1630}, Quantity: 5}
|
||||||
} else {
|
} else {
|
||||||
tktStack = mhfpacket.WarehouseStack{ItemID: 0x065F, Quantity: 5}
|
tktStack = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 1631}, Quantity: 5}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addWarehouseGift(s, "item", tktStack)
|
addWarehouseItem(s, tktStack)
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
bf.WriteUint16(total)
|
bf.WriteUint16(total)
|
||||||
bf.WriteUint16(redeemed)
|
bf.WriteUint16(redeemed)
|
||||||
bf.WriteUint16(0)
|
bf.WriteUint16(0)
|
||||||
bf.WriteUint32(0) // Unk, but has possible values
|
bf.WriteUint16(tktStack.Item.ItemID)
|
||||||
|
bf.WriteUint16(tktStack.Quantity)
|
||||||
bf.WriteUint32(uint32(TimeWeekStart().Unix()))
|
bf.WriteUint32(uint32(TimeWeekStart().Unix()))
|
||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
@@ -1103,9 +1048,11 @@ func handleMsgMhfStampcardStamp(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
pkt := p.(*mhfpacket.MsgMhfStampcardStamp)
|
pkt := p.(*mhfpacket.MsgMhfStampcardStamp)
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
bf.WriteUint16(pkt.HR)
|
bf.WriteUint16(pkt.HR)
|
||||||
bf.WriteUint16(pkt.GR)
|
|
||||||
var stamps uint16
|
var stamps uint16
|
||||||
_ = s.server.db.QueryRow(`SELECT stampcard FROM characters WHERE id = $1`, s.charID).Scan(&stamps)
|
_ = s.server.db.QueryRow(`SELECT stampcard FROM characters WHERE id = $1`, s.charID).Scan(&stamps)
|
||||||
|
if _config.ErupeConfig.RealClientMode >= _config.G1 {
|
||||||
|
bf.WriteUint16(pkt.GR)
|
||||||
|
}
|
||||||
bf.WriteUint16(stamps)
|
bf.WriteUint16(stamps)
|
||||||
stamps += pkt.Stamps
|
stamps += pkt.Stamps
|
||||||
bf.WriteUint16(stamps)
|
bf.WriteUint16(stamps)
|
||||||
@@ -1115,13 +1062,13 @@ func handleMsgMhfStampcardStamp(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
bf.WriteUint16(pkt.Reward2)
|
bf.WriteUint16(pkt.Reward2)
|
||||||
bf.WriteUint16(pkt.Item2)
|
bf.WriteUint16(pkt.Item2)
|
||||||
bf.WriteUint16(pkt.Quantity2)
|
bf.WriteUint16(pkt.Quantity2)
|
||||||
addWarehouseGift(s, "item", mhfpacket.WarehouseStack{ItemID: pkt.Item2, Quantity: pkt.Quantity2})
|
addWarehouseItem(s, mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: pkt.Item2}, Quantity: pkt.Quantity2})
|
||||||
} else if stamps%15 == 0 {
|
} else if stamps%15 == 0 {
|
||||||
bf.WriteUint16(1)
|
bf.WriteUint16(1)
|
||||||
bf.WriteUint16(pkt.Reward1)
|
bf.WriteUint16(pkt.Reward1)
|
||||||
bf.WriteUint16(pkt.Item1)
|
bf.WriteUint16(pkt.Item1)
|
||||||
bf.WriteUint16(pkt.Quantity1)
|
bf.WriteUint16(pkt.Quantity1)
|
||||||
addWarehouseGift(s, "item", mhfpacket.WarehouseStack{ItemID: pkt.Item1, Quantity: pkt.Quantity1})
|
addWarehouseItem(s, mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: pkt.Item1}, Quantity: pkt.Quantity1})
|
||||||
} else {
|
} else {
|
||||||
bf.WriteBytes(make([]byte, 8))
|
bf.WriteBytes(make([]byte, 8))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,35 +7,47 @@ import (
|
|||||||
"erupe-ce/network/mhfpacket"
|
"erupe-ce/network/mhfpacket"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Handler BBS handles all the interactions with the for the screenshot sending to bulitin board functionality. For it to work it requires the API to be hosted somehwere. This implementation supports discord.
|
||||||
|
|
||||||
|
// Checks the status of the user to see if they can use Bulitin Board yet
|
||||||
func handleMsgMhfGetBbsUserStatus(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfGetBbsUserStatus(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
//Post Screenshot pauses till this succeedes
|
||||||
pkt := p.(*mhfpacket.MsgMhfGetBbsUserStatus)
|
pkt := p.(*mhfpacket.MsgMhfGetBbsUserStatus)
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
bf.WriteUint32(200)
|
bf.WriteUint32(200) //HTTP Status Codes //200 Success //404 You wont be able to post for a certain amount of time after creating your character //401/500 A error occured server side
|
||||||
bf.WriteUint32(0)
|
bf.WriteUint32(0)
|
||||||
bf.WriteUint32(0)
|
bf.WriteUint32(0)
|
||||||
bf.WriteUint32(0)
|
bf.WriteUint32(0)
|
||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks the status of Bultin Board Server to see if authenticated
|
||||||
func handleMsgMhfGetBbsSnsStatus(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfGetBbsSnsStatus(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfGetBbsSnsStatus)
|
pkt := p.(*mhfpacket.MsgMhfGetBbsSnsStatus)
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
bf.WriteUint32(200)
|
bf.WriteUint32(200) //200 Success //4XX Authentication has expired Please re-authenticate //5XX
|
||||||
bf.WriteUint32(401)
|
bf.WriteUint32(401) //unk http status?
|
||||||
bf.WriteUint32(401)
|
bf.WriteUint32(401) //unk http status?
|
||||||
bf.WriteUint32(0)
|
bf.WriteUint32(0)
|
||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tells the game client what host port and gives the bultin board article a token
|
||||||
func handleMsgMhfApplyBbsArticle(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfApplyBbsArticle(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfApplyBbsArticle)
|
pkt := p.(*mhfpacket.MsgMhfApplyBbsArticle)
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
articleToken := token.Generate(40)
|
articleToken := token.Generate(40)
|
||||||
bf.WriteUint32(200)
|
|
||||||
bf.WriteUint32(80)
|
bf.WriteUint32(200) //http status //200 success //4XX An error occured server side
|
||||||
|
bf.WriteUint32(s.server.erupeConfig.Screenshots.Port)
|
||||||
bf.WriteUint32(0)
|
bf.WriteUint32(0)
|
||||||
bf.WriteUint32(0)
|
bf.WriteUint32(0)
|
||||||
bf.WriteBytes(stringsupport.PaddedString(articleToken, 64, false))
|
bf.WriteBytes(stringsupport.PaddedString(articleToken, 64, false))
|
||||||
bf.WriteBytes(stringsupport.PaddedString(s.server.erupeConfig.ScreenshotAPIURL, 64, false))
|
bf.WriteBytes(stringsupport.PaddedString(s.server.erupeConfig.Screenshots.Host, 64, false))
|
||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
//pkt.unk1[3] == Changes sometimes?
|
||||||
|
if s.server.erupeConfig.Screenshots.Enabled && s.server.erupeConfig.Discord.Enabled {
|
||||||
|
s.server.DiscordScreenShotSend(pkt.Name, pkt.Title, pkt.Description, articleToken)
|
||||||
|
}
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
"erupe-ce/common/mhfcourse"
|
"erupe-ce/common/mhfcourse"
|
||||||
ps "erupe-ce/common/pascalstring"
|
ps "erupe-ce/common/pascalstring"
|
||||||
|
_config "erupe-ce/config"
|
||||||
"erupe-ce/network/mhfpacket"
|
"erupe-ce/network/mhfpacket"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
@@ -92,10 +93,11 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
if mhfcourse.CourseExists(30, s.courses) {
|
if mhfcourse.CourseExists(30, s.courses) {
|
||||||
cafeTime = uint32(TimeAdjusted().Unix()) - uint32(s.sessionStart) + cafeTime
|
cafeTime = uint32(TimeAdjusted().Unix()) - uint32(s.sessionStart) + cafeTime
|
||||||
}
|
}
|
||||||
bf.WriteUint32(cafeTime) // Total cafe time
|
bf.WriteUint32(cafeTime)
|
||||||
|
if _config.ErupeConfig.RealClientMode >= _config.ZZ {
|
||||||
bf.WriteUint16(0)
|
bf.WriteUint16(0)
|
||||||
ps.Uint16(bf, fmt.Sprintf(s.server.i18n.cafe.reset, int(cafeReset.Month()), cafeReset.Day()), true)
|
ps.Uint16(bf, fmt.Sprintf(s.server.i18n.cafe.reset, int(cafeReset.Month()), cafeReset.Day()), true)
|
||||||
|
}
|
||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -478,7 +478,7 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
m := binpacket.MsgBinChat{
|
m := binpacket.MsgBinChat{
|
||||||
Type: BinaryMessageTypeChat,
|
Type: BinaryMessageTypeChat,
|
||||||
Flags: 4,
|
Flags: 4,
|
||||||
Message: fmt.Sprintf(`%d`, token.RNG().Intn(100)+1),
|
Message: fmt.Sprintf(`%d`, token.RNG.Intn(100)+1),
|
||||||
SenderName: author,
|
SenderName: author,
|
||||||
}
|
}
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const (
|
|||||||
pGardenData // +68
|
pGardenData // +68
|
||||||
pWeaponType // +1
|
pWeaponType // +1
|
||||||
pWeaponID // +2
|
pWeaponID // +2
|
||||||
pHRP // +2
|
pHR // +2
|
||||||
pGRP // +4
|
pGRP // +4
|
||||||
pKQF // +8
|
pKQF // +8
|
||||||
lBookshelfData
|
lBookshelfData
|
||||||
@@ -47,7 +47,7 @@ type CharacterSaveData struct {
|
|||||||
GardenData []byte
|
GardenData []byte
|
||||||
WeaponType uint8
|
WeaponType uint8
|
||||||
WeaponID uint16
|
WeaponID uint16
|
||||||
HRP uint16
|
HR uint16
|
||||||
GR uint16
|
GR uint16
|
||||||
KQF []byte
|
KQF []byte
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ func getPointers() map[SavePointer]int {
|
|||||||
pointers[pWeaponType] = 128789
|
pointers[pWeaponType] = 128789
|
||||||
pointers[pHouseTier] = 129900
|
pointers[pHouseTier] = 129900
|
||||||
pointers[pToreData] = 130228
|
pointers[pToreData] = 130228
|
||||||
pointers[pHRP] = 130550
|
pointers[pHR] = 130550
|
||||||
pointers[pGRP] = 130556
|
pointers[pGRP] = 130556
|
||||||
pointers[pHouseData] = 130561
|
pointers[pHouseData] = 130561
|
||||||
pointers[pBookshelfData] = 139928
|
pointers[pBookshelfData] = 139928
|
||||||
@@ -78,10 +78,10 @@ func getPointers() map[SavePointer]int {
|
|||||||
pointers[pWeaponType] = 92789
|
pointers[pWeaponType] = 92789
|
||||||
pointers[pHouseTier] = 93900
|
pointers[pHouseTier] = 93900
|
||||||
pointers[pToreData] = 94228
|
pointers[pToreData] = 94228
|
||||||
pointers[pHRP] = 94550
|
pointers[pHR] = 94550
|
||||||
pointers[pGRP] = 94556
|
pointers[pGRP] = 94556
|
||||||
pointers[pHouseData] = 94561
|
pointers[pHouseData] = 94561
|
||||||
pointers[pBookshelfData] = 103928
|
pointers[pBookshelfData] = 89118 // TODO: fix bookshelf data pointer
|
||||||
pointers[pGalleryData] = 104064
|
pointers[pGalleryData] = 104064
|
||||||
pointers[pGardenData] = 106424
|
pointers[pGardenData] = 106424
|
||||||
pointers[pRP] = 106614
|
pointers[pRP] = 106614
|
||||||
@@ -91,9 +91,9 @@ func getPointers() map[SavePointer]int {
|
|||||||
pointers[pWeaponType] = 60789
|
pointers[pWeaponType] = 60789
|
||||||
pointers[pHouseTier] = 61900
|
pointers[pHouseTier] = 61900
|
||||||
pointers[pToreData] = 62228
|
pointers[pToreData] = 62228
|
||||||
pointers[pHRP] = 62550
|
pointers[pHR] = 62550
|
||||||
pointers[pHouseData] = 62561
|
pointers[pHouseData] = 62561
|
||||||
pointers[pBookshelfData] = 57118 // This pointer only half works
|
pointers[pBookshelfData] = 57118 // TODO: fix bookshelf data pointer
|
||||||
pointers[pGalleryData] = 72064
|
pointers[pGalleryData] = 72064
|
||||||
pointers[pGardenData] = 74424
|
pointers[pGardenData] = 74424
|
||||||
pointers[pRP] = 74614
|
pointers[pRP] = 74614
|
||||||
@@ -102,9 +102,9 @@ func getPointers() map[SavePointer]int {
|
|||||||
pointers[pWeaponType] = 12789
|
pointers[pWeaponType] = 12789
|
||||||
pointers[pHouseTier] = 13900
|
pointers[pHouseTier] = 13900
|
||||||
pointers[pToreData] = 14228
|
pointers[pToreData] = 14228
|
||||||
pointers[pHRP] = 14550
|
pointers[pHR] = 14550
|
||||||
pointers[pHouseData] = 14561
|
pointers[pHouseData] = 14561
|
||||||
pointers[pBookshelfData] = 9118 // Probably same here
|
pointers[pBookshelfData] = 9118 // TODO: fix bookshelf data pointer
|
||||||
pointers[pGalleryData] = 24064
|
pointers[pGalleryData] = 24064
|
||||||
pointers[pGardenData] = 26424
|
pointers[pGardenData] = 26424
|
||||||
pointers[pRP] = 26614
|
pointers[pRP] = 26614
|
||||||
@@ -174,8 +174,8 @@ func (save *CharacterSaveData) Save(s *Session) {
|
|||||||
save.compSave = save.decompSave
|
save.compSave = save.decompSave
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := s.server.db.Exec(`UPDATE characters SET savedata=$1, is_new_character=false, hrp=$2, gr=$3, is_female=$4, weapon_type=$5, weapon_id=$6 WHERE id=$7
|
_, err := s.server.db.Exec(`UPDATE characters SET savedata=$1, is_new_character=false, hr=$2, gr=$3, is_female=$4, weapon_type=$5, weapon_id=$6 WHERE id=$7
|
||||||
`, save.compSave, save.HRP, save.GR, save.Gender, save.WeaponType, save.WeaponID, save.CharID)
|
`, save.compSave, save.HR, save.GR, save.Gender, save.WeaponType, save.WeaponID, save.CharID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to update savedata", zap.Error(err), zap.Uint32("charID", save.CharID))
|
s.logger.Error("Failed to update savedata", zap.Error(err), zap.Uint32("charID", save.CharID))
|
||||||
}
|
}
|
||||||
@@ -233,9 +233,9 @@ func (save *CharacterSaveData) updateStructWithSaveData() {
|
|||||||
save.GardenData = save.decompSave[save.Pointers[pGardenData] : save.Pointers[pGardenData]+68]
|
save.GardenData = save.decompSave[save.Pointers[pGardenData] : save.Pointers[pGardenData]+68]
|
||||||
save.WeaponType = save.decompSave[save.Pointers[pWeaponType]]
|
save.WeaponType = save.decompSave[save.Pointers[pWeaponType]]
|
||||||
save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pWeaponID] : save.Pointers[pWeaponID]+2])
|
save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pWeaponID] : save.Pointers[pWeaponID]+2])
|
||||||
save.HRP = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pHRP] : save.Pointers[pHRP]+2])
|
save.HR = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pHR] : save.Pointers[pHR]+2])
|
||||||
if _config.ErupeConfig.RealClientMode >= _config.G1 {
|
if _config.ErupeConfig.RealClientMode >= _config.G1 {
|
||||||
if save.HRP == uint16(999) {
|
if save.HR == uint16(999) {
|
||||||
save.GR = grpToGR(int(binary.LittleEndian.Uint32(save.decompSave[save.Pointers[pGRP] : save.Pointers[pGRP]+4])))
|
save.GR = grpToGR(int(binary.LittleEndian.Uint32(save.decompSave[save.Pointers[pGRP] : save.Pointers[pGRP]+4])))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,8 @@ func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
var temp activeFeature
|
var temp activeFeature
|
||||||
err := s.server.db.QueryRowx(`SELECT start_time, featured FROM feature_weapon WHERE start_time=$1`, t).StructScan(&temp)
|
err := s.server.db.QueryRowx(`SELECT start_time, featured FROM feature_weapon WHERE start_time=$1`, t).StructScan(&temp)
|
||||||
if err != nil || temp.StartTime.IsZero() {
|
if err != nil || temp.StartTime.IsZero() {
|
||||||
temp = generateFeatureWeapons(s.server.erupeConfig.GameplayOptions.FeaturedWeapons)
|
weapons := token.RNG.Intn(s.server.erupeConfig.GameplayOptions.MaxFeatureWeapons-s.server.erupeConfig.GameplayOptions.MinFeatureWeapons+1) + s.server.erupeConfig.GameplayOptions.MinFeatureWeapons
|
||||||
|
temp = generateFeatureWeapons(weapons)
|
||||||
temp.StartTime = t
|
temp.StartTime = t
|
||||||
s.server.db.Exec(`INSERT INTO feature_weapon VALUES ($1, $2)`, temp.StartTime, temp.ActiveFeatures)
|
s.server.db.Exec(`INSERT INTO feature_weapon VALUES ($1, $2)`, temp.StartTime, temp.ActiveFeatures)
|
||||||
}
|
}
|
||||||
@@ -101,8 +102,7 @@ func generateFeatureWeapons(count int) activeFeature {
|
|||||||
nums := make([]int, 0)
|
nums := make([]int, 0)
|
||||||
var result int
|
var result int
|
||||||
for len(nums) < count {
|
for len(nums) < count {
|
||||||
rng := token.RNG()
|
num := token.RNG.Intn(_max)
|
||||||
num := rng.Intn(_max)
|
|
||||||
exist := false
|
exist := false
|
||||||
for _, v := range nums {
|
for _, v := range nums {
|
||||||
if v == num {
|
if v == num {
|
||||||
|
|||||||
@@ -559,7 +559,7 @@ func handleMsgMhfEntryFesta(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
team := uint32(token.RNG().Intn(2))
|
team := uint32(token.RNG.Intn(2))
|
||||||
switch team {
|
switch team {
|
||||||
case 0:
|
case 0:
|
||||||
s.server.db.Exec("INSERT INTO festa_registrations VALUES ($1, 'blue')", guild.ID)
|
s.server.db.Exec("INSERT INTO festa_registrations VALUES ($1, 'blue')", guild.ID)
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package channelserver
|
|||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"encoding/binary"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"erupe-ce/common/mhfitem"
|
||||||
_config "erupe-ce/config"
|
_config "erupe-ce/config"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
@@ -51,6 +51,8 @@ type Guild struct {
|
|||||||
MemberCount uint16 `db:"member_count"`
|
MemberCount uint16 `db:"member_count"`
|
||||||
RankRP uint32 `db:"rank_rp"`
|
RankRP uint32 `db:"rank_rp"`
|
||||||
EventRP uint32 `db:"event_rp"`
|
EventRP uint32 `db:"event_rp"`
|
||||||
|
RoomRP uint16 `db:"room_rp"`
|
||||||
|
RoomExpiry time.Time `db:"room_expiry"`
|
||||||
Comment string `db:"comment"`
|
Comment string `db:"comment"`
|
||||||
PugiName1 string `db:"pugi_name_1"`
|
PugiName1 string `db:"pugi_name_1"`
|
||||||
PugiName2 string `db:"pugi_name_2"`
|
PugiName2 string `db:"pugi_name_2"`
|
||||||
@@ -153,6 +155,8 @@ SELECT
|
|||||||
g.name,
|
g.name,
|
||||||
rank_rp,
|
rank_rp,
|
||||||
event_rp,
|
event_rp,
|
||||||
|
room_rp,
|
||||||
|
COALESCE(room_expiry, '1970-01-01') AS room_expiry,
|
||||||
main_motto,
|
main_motto,
|
||||||
sub_motto,
|
sub_motto,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -706,7 +710,7 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
bf.WriteUint32(uint32(response))
|
bf.WriteUint32(uint32(response))
|
||||||
case mhfpacket.OperateGuildDonateRank:
|
case mhfpacket.OperateGuildDonateRank:
|
||||||
bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, false))
|
bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, 0))
|
||||||
case mhfpacket.OperateGuildSetApplicationDeny:
|
case mhfpacket.OperateGuildSetApplicationDeny:
|
||||||
s.server.db.Exec("UPDATE guilds SET recruiting=false WHERE id=$1", guild.ID)
|
s.server.db.Exec("UPDATE guilds SET recruiting=false WHERE id=$1", guild.ID)
|
||||||
case mhfpacket.OperateGuildSetApplicationAllow:
|
case mhfpacket.OperateGuildSetApplicationAllow:
|
||||||
@@ -747,10 +751,11 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
// TODO: This doesn't implement blocking, if someone unlocked the same outfit at the same time
|
// TODO: This doesn't implement blocking, if someone unlocked the same outfit at the same time
|
||||||
s.server.db.Exec(`UPDATE guilds SET pugi_outfits=pugi_outfits+$1 WHERE id=$2`, int(math.Pow(float64(pkt.Data1.ReadUint32()), 2)), guild.ID)
|
s.server.db.Exec(`UPDATE guilds SET pugi_outfits=pugi_outfits+$1 WHERE id=$2`, int(math.Pow(float64(pkt.Data1.ReadUint32()), 2)), guild.ID)
|
||||||
case mhfpacket.OperateGuildDonateRoom:
|
case mhfpacket.OperateGuildDonateRoom:
|
||||||
// TODO: Where does this go?
|
quantity := uint16(pkt.Data1.ReadUint32())
|
||||||
|
bf.WriteBytes(handleDonateRP(s, quantity, guild, 2))
|
||||||
case mhfpacket.OperateGuildDonateEvent:
|
case mhfpacket.OperateGuildDonateEvent:
|
||||||
quantity := uint16(pkt.Data1.ReadUint32())
|
quantity := uint16(pkt.Data1.ReadUint32())
|
||||||
bf.WriteBytes(handleDonateRP(s, quantity, guild, true))
|
bf.WriteBytes(handleDonateRP(s, quantity, guild, 1))
|
||||||
// TODO: Move this value onto rp_yesterday and reset to 0... daily?
|
// TODO: Move this value onto rp_yesterday and reset to 0... daily?
|
||||||
s.server.db.Exec(`UPDATE guild_characters SET rp_today=rp_today+$1 WHERE character_id=$2`, quantity, s.charID)
|
s.server.db.Exec(`UPDATE guild_characters SET rp_today=rp_today+$1 WHERE character_id=$2`, quantity, s.charID)
|
||||||
case mhfpacket.OperateGuildEventExchange:
|
case mhfpacket.OperateGuildEventExchange:
|
||||||
@@ -794,20 +799,37 @@ func handleChangePugi(s *Session, outfit uint8, guild *Guild, num int) {
|
|||||||
guild.Save(s)
|
guild.Save(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleDonateRP(s *Session, amount uint16, guild *Guild, isEvent bool) []byte {
|
func handleDonateRP(s *Session, amount uint16, guild *Guild, _type int) []byte {
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
bf.WriteUint32(0)
|
bf.WriteUint32(0)
|
||||||
saveData, err := GetCharacterSaveData(s, s.charID)
|
saveData, err := GetCharacterSaveData(s, s.charID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return bf.Data()
|
return bf.Data()
|
||||||
}
|
}
|
||||||
|
var resetRoom bool
|
||||||
|
if _type == 2 {
|
||||||
|
var currentRP uint16
|
||||||
|
s.server.db.QueryRow(`SELECT room_rp FROM guilds WHERE id = $1`, guild.ID).Scan(¤tRP)
|
||||||
|
if currentRP+amount >= 30 {
|
||||||
|
amount = 30 - currentRP
|
||||||
|
resetRoom = true
|
||||||
|
}
|
||||||
|
}
|
||||||
saveData.RP -= amount
|
saveData.RP -= amount
|
||||||
saveData.Save(s)
|
saveData.Save(s)
|
||||||
updateSQL := "UPDATE guilds SET rank_rp = rank_rp + $1 WHERE id = $2"
|
switch _type {
|
||||||
if isEvent {
|
case 0:
|
||||||
updateSQL = "UPDATE guilds SET event_rp = event_rp + $1 WHERE id = $2"
|
s.server.db.Exec(`UPDATE guilds SET rank_rp = rank_rp + $1 WHERE id = $2`, amount, guild.ID)
|
||||||
|
case 1:
|
||||||
|
s.server.db.Exec(`UPDATE guilds SET event_rp = event_rp + $1 WHERE id = $2`, amount, guild.ID)
|
||||||
|
case 2:
|
||||||
|
if resetRoom {
|
||||||
|
s.server.db.Exec(`UPDATE guilds SET room_rp = 0 WHERE id = $1`, guild.ID)
|
||||||
|
s.server.db.Exec(`UPDATE guilds SET room_expiry = $1 WHERE id = $2`, TimeAdjusted().Add(time.Hour*24*7), guild.ID)
|
||||||
|
} else {
|
||||||
|
s.server.db.Exec(`UPDATE guilds SET room_rp = room_rp + $1 WHERE id = $2`, amount, guild.ID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s.server.db.Exec(updateSQL, amount, guild.ID)
|
|
||||||
bf.Seek(0, 0)
|
bf.Seek(0, 0)
|
||||||
bf.WriteUint32(uint32(saveData.RP))
|
bf.WriteUint32(uint32(saveData.RP))
|
||||||
return bf.Data()
|
return bf.Data()
|
||||||
@@ -1001,8 +1023,8 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
bf.WriteUint8(limit)
|
bf.WriteUint8(limit)
|
||||||
|
|
||||||
bf.WriteUint32(55000)
|
bf.WriteUint32(55000)
|
||||||
bf.WriteUint32(0)
|
bf.WriteUint32(uint32(guild.RoomExpiry.Unix()))
|
||||||
bf.WriteUint16(0) // Changing Room RP
|
bf.WriteUint16(guild.RoomRP)
|
||||||
bf.WriteUint16(0) // Ignored
|
bf.WriteUint16(0) // Ignored
|
||||||
|
|
||||||
if guild.AllianceID > 0 {
|
if guild.AllianceID > 0 {
|
||||||
@@ -1075,7 +1097,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
for _, applicant := range applicants {
|
for _, applicant := range applicants {
|
||||||
bf.WriteUint32(applicant.CharID)
|
bf.WriteUint32(applicant.CharID)
|
||||||
bf.WriteUint32(0)
|
bf.WriteUint32(0)
|
||||||
bf.WriteUint16(applicant.HRP)
|
bf.WriteUint16(applicant.HR)
|
||||||
bf.WriteUint16(applicant.GR)
|
bf.WriteUint16(applicant.GR)
|
||||||
ps.Uint8(bf, applicant.Name, true)
|
ps.Uint8(bf, applicant.Name, true)
|
||||||
}
|
}
|
||||||
@@ -1435,7 +1457,7 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
for _, member := range guildMembers {
|
for _, member := range guildMembers {
|
||||||
bf.WriteUint32(member.CharID)
|
bf.WriteUint32(member.CharID)
|
||||||
bf.WriteUint16(member.HRP)
|
bf.WriteUint16(member.HR)
|
||||||
if s.server.erupeConfig.RealClientMode >= _config.G10 {
|
if s.server.erupeConfig.RealClientMode >= _config.G10 {
|
||||||
bf.WriteUint16(member.GR)
|
bf.WriteUint16(member.GR)
|
||||||
}
|
}
|
||||||
@@ -1554,100 +1576,34 @@ func handleMsgMhfGetGuildTargetMemberNum(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfEnumerateGuildItem(s *Session, p mhfpacket.MHFPacket) {
|
func guildGetItems(s *Session, guildID uint32) []mhfitem.MHFItemStack {
|
||||||
pkt := p.(*mhfpacket.MsgMhfEnumerateGuildItem)
|
var data []byte
|
||||||
var boxContents []byte
|
var items []mhfitem.MHFItemStack
|
||||||
bf := byteframe.NewByteFrame()
|
s.server.db.QueryRow(`SELECT item_box FROM guilds WHERE id=$1`, guildID).Scan(&data)
|
||||||
err := s.server.db.QueryRow("SELECT item_box FROM guilds WHERE id = $1", pkt.GuildID).Scan(&boxContents)
|
if len(data) > 0 {
|
||||||
if err != nil {
|
box := byteframe.NewByteFrameFromBytes(data)
|
||||||
s.logger.Error("Failed to get guild item box contents from db", zap.Error(err))
|
numStacks := box.ReadUint16()
|
||||||
bf.WriteBytes(make([]byte, 4))
|
box.ReadUint16() // Unused
|
||||||
} else {
|
for i := 0; i < int(numStacks); i++ {
|
||||||
if len(boxContents) == 0 {
|
items = append(items, mhfitem.ReadWarehouseItem(box))
|
||||||
bf.WriteBytes(make([]byte, 4))
|
|
||||||
} else {
|
|
||||||
amount := len(boxContents) / 4
|
|
||||||
bf.WriteUint16(uint16(amount))
|
|
||||||
bf.WriteUint32(0x00)
|
|
||||||
bf.WriteUint16(0x00)
|
|
||||||
for i := 0; i < amount; i++ {
|
|
||||||
bf.WriteUint32(binary.BigEndian.Uint32(boxContents[i*4 : i*4+4]))
|
|
||||||
if i+1 != amount {
|
|
||||||
bf.WriteUint64(0x00)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return items
|
||||||
}
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Item struct {
|
func handleMsgMhfEnumerateGuildItem(s *Session, p mhfpacket.MHFPacket) {
|
||||||
ItemId uint16
|
pkt := p.(*mhfpacket.MsgMhfEnumerateGuildItem)
|
||||||
Amount uint16
|
items := guildGetItems(s, pkt.GuildID)
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
bf.WriteBytes(mhfitem.SerializeWarehouseItems(items))
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfUpdateGuildItem(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfUpdateGuildItem(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfUpdateGuildItem)
|
pkt := p.(*mhfpacket.MsgMhfUpdateGuildItem)
|
||||||
|
newStacks := mhfitem.DiffItemStacks(guildGetItems(s, pkt.GuildID), pkt.UpdatedItems)
|
||||||
// Get item cache from DB
|
s.server.db.Exec(`UPDATE guilds SET item_box=$1 WHERE id=$2`, mhfitem.SerializeWarehouseItems(newStacks), pkt.GuildID)
|
||||||
var boxContents []byte
|
|
||||||
var oldItems []Item
|
|
||||||
err := s.server.db.QueryRow("SELECT item_box FROM guilds WHERE id = $1", pkt.GuildID).Scan(&boxContents)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to get guild item box contents from db", zap.Error(err))
|
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
return
|
|
||||||
} else {
|
|
||||||
amount := len(boxContents) / 4
|
|
||||||
oldItems = make([]Item, amount)
|
|
||||||
for i := 0; i < amount; i++ {
|
|
||||||
oldItems[i].ItemId = binary.BigEndian.Uint16(boxContents[i*4 : i*4+2])
|
|
||||||
oldItems[i].Amount = binary.BigEndian.Uint16(boxContents[i*4+2 : i*4+4])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update item stacks
|
|
||||||
newItems := make([]Item, len(oldItems))
|
|
||||||
copy(newItems, oldItems)
|
|
||||||
for i := 0; i < len(pkt.Items); i++ {
|
|
||||||
for j := 0; j <= len(oldItems); j++ {
|
|
||||||
if j == len(oldItems) {
|
|
||||||
var newItem Item
|
|
||||||
newItem.ItemId = pkt.Items[i].ItemID
|
|
||||||
newItem.Amount = pkt.Items[i].Amount
|
|
||||||
newItems = append(newItems, newItem)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if pkt.Items[i].ItemID == oldItems[j].ItemId {
|
|
||||||
newItems[j].Amount = pkt.Items[i].Amount
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete empty item stacks
|
|
||||||
for i := len(newItems) - 1; i >= 0; i-- {
|
|
||||||
if int(newItems[i].Amount) == 0 {
|
|
||||||
copy(newItems[i:], newItems[i+1:])
|
|
||||||
newItems[len(newItems)-1] = make([]Item, 1)[0]
|
|
||||||
newItems = newItems[:len(newItems)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new item cache
|
|
||||||
bf := byteframe.NewByteFrame()
|
|
||||||
for i := 0; i < len(newItems); i++ {
|
|
||||||
bf.WriteUint16(newItems[i].ItemId)
|
|
||||||
bf.WriteUint16(newItems[i].Amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload new item cache
|
|
||||||
_, err = s.server.db.Exec("UPDATE guilds SET item_box = $1 WHERE id = $2", bf.Data(), pkt.GuildID)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to update guild item box contents in db", zap.Error(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfUpdateGuildIcon(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfUpdateGuildIcon(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ type GuildMember struct {
|
|||||||
Recruiter bool `db:"recruiter"`
|
Recruiter bool `db:"recruiter"`
|
||||||
AvoidLeadership bool `db:"avoid_leadership"`
|
AvoidLeadership bool `db:"avoid_leadership"`
|
||||||
IsLeader bool `db:"is_leader"`
|
IsLeader bool `db:"is_leader"`
|
||||||
HRP uint16 `db:"hrp"`
|
HR uint16 `db:"hr"`
|
||||||
GR uint16 `db:"gr"`
|
GR uint16 `db:"gr"`
|
||||||
WeaponID uint16 `db:"weapon_id"`
|
WeaponID uint16 `db:"weapon_id"`
|
||||||
WeaponType uint8 `db:"weapon_type"`
|
WeaponType uint8 `db:"weapon_type"`
|
||||||
@@ -74,7 +74,7 @@ SELECT * FROM (
|
|||||||
c.last_login,
|
c.last_login,
|
||||||
COALESCE(recruiter, false) AS recruiter,
|
COALESCE(recruiter, false) AS recruiter,
|
||||||
COALESCE(avoid_leadership, false) AS avoid_leadership,
|
COALESCE(avoid_leadership, false) AS avoid_leadership,
|
||||||
c.hrp,
|
c.hr,
|
||||||
c.gr,
|
c.gr,
|
||||||
c.weapon_id,
|
c.weapon_id,
|
||||||
c.weapon_type,
|
c.weapon_type,
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ func handleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rows, err := s.server.db.Queryx(`
|
rows, err := s.server.db.Queryx(`
|
||||||
SELECT c.id, c.name, c.hrp, c.gr, ga.actor_id
|
SELECT c.id, c.name, c.hr, c.gr, ga.actor_id
|
||||||
FROM guild_applications ga
|
FROM guild_applications ga
|
||||||
JOIN characters c ON c.id = ga.character_id
|
JOIN characters c ON c.id = ga.character_id
|
||||||
WHERE ga.guild_id = $1 AND ga.application_type = 'invited'
|
WHERE ga.guild_id = $1 AND ga.application_type = 'invited'
|
||||||
@@ -230,9 +230,9 @@ func handleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var charName string
|
var charName string
|
||||||
var charID, actorID uint32
|
var charID, actorID uint32
|
||||||
var hrp, gr uint16
|
var HR, GR uint16
|
||||||
|
|
||||||
err = rows.Scan(&charID, &charName, &hrp, &gr, &actorID)
|
err = rows.Scan(&charID, &charName, &HR, &GR, &actorID)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
doAckSimpleFail(s, pkt.AckHandle, nil)
|
doAckSimpleFail(s, pkt.AckHandle, nil)
|
||||||
@@ -246,8 +246,8 @@ func handleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
bf.WriteUint32(actorID)
|
bf.WriteUint32(actorID)
|
||||||
bf.WriteUint32(charID)
|
bf.WriteUint32(charID)
|
||||||
bf.WriteUint32(uint32(TimeAdjusted().Unix()))
|
bf.WriteUint32(uint32(TimeAdjusted().Unix()))
|
||||||
bf.WriteUint16(hrp) // HR?
|
bf.WriteUint16(HR) // HR?
|
||||||
bf.WriteUint16(gr) // GR?
|
bf.WriteUint16(GR) // GR?
|
||||||
bf.WriteBytes(stringsupport.PaddedString(charName, 32, true))
|
bf.WriteBytes(stringsupport.PaddedString(charName, 32, true))
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package channelserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/common/mhfitem"
|
||||||
ps "erupe-ce/common/pascalstring"
|
ps "erupe-ce/common/pascalstring"
|
||||||
"erupe-ce/common/stringsupport"
|
"erupe-ce/common/stringsupport"
|
||||||
|
"erupe-ce/common/token"
|
||||||
_config "erupe-ce/config"
|
_config "erupe-ce/config"
|
||||||
"erupe-ce/network/mhfpacket"
|
"erupe-ce/network/mhfpacket"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -45,7 +47,7 @@ func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
type HouseData struct {
|
type HouseData struct {
|
||||||
CharID uint32 `db:"id"`
|
CharID uint32 `db:"id"`
|
||||||
HRP uint16 `db:"hrp"`
|
HR uint16 `db:"hr"`
|
||||||
GR uint16 `db:"gr"`
|
GR uint16 `db:"gr"`
|
||||||
Name string `db:"name"`
|
Name string `db:"name"`
|
||||||
HouseState uint8 `db:"house_state"`
|
HouseState uint8 `db:"house_state"`
|
||||||
@@ -57,7 +59,7 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
bf.WriteUint16(0)
|
bf.WriteUint16(0)
|
||||||
var houses []HouseData
|
var houses []HouseData
|
||||||
houseQuery := `SELECT c.id, hrp, gr, name, COALESCE(ub.house_state, 2) as house_state, COALESCE(ub.house_password, '') as house_password
|
houseQuery := `SELECT c.id, hr, gr, name, COALESCE(ub.house_state, 2) as house_state, COALESCE(ub.house_password, '') as house_password
|
||||||
FROM characters c LEFT JOIN user_binary ub ON ub.id = c.id WHERE c.id=$1`
|
FROM characters c LEFT JOIN user_binary ub ON ub.id = c.id WHERE c.id=$1`
|
||||||
switch pkt.Method {
|
switch pkt.Method {
|
||||||
case 1:
|
case 1:
|
||||||
@@ -90,7 +92,7 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
houseQuery = `SELECT c.id, hrp, gr, name, COALESCE(ub.house_state, 2) as house_state, COALESCE(ub.house_password, '') as house_password
|
houseQuery = `SELECT c.id, hr, gr, name, COALESCE(ub.house_state, 2) as house_state, COALESCE(ub.house_password, '') as house_password
|
||||||
FROM characters c LEFT JOIN user_binary ub ON ub.id = c.id WHERE name ILIKE $1`
|
FROM characters c LEFT JOIN user_binary ub ON ub.id = c.id WHERE name ILIKE $1`
|
||||||
house := HouseData{}
|
house := HouseData{}
|
||||||
rows, _ := s.server.db.Queryx(houseQuery, fmt.Sprintf(`%%%s%%`, pkt.Name))
|
rows, _ := s.server.db.Queryx(houseQuery, fmt.Sprintf(`%%%s%%`, pkt.Name))
|
||||||
@@ -118,7 +120,7 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
} else {
|
} else {
|
||||||
bf.WriteUint8(0)
|
bf.WriteUint8(0)
|
||||||
}
|
}
|
||||||
bf.WriteUint16(house.HRP)
|
bf.WriteUint16(house.HR)
|
||||||
if _config.ErupeConfig.RealClientMode >= _config.G10 {
|
if _config.ErupeConfig.RealClientMode >= _config.G10 {
|
||||||
bf.WriteUint16(house.GR)
|
bf.WriteUint16(house.GR)
|
||||||
}
|
}
|
||||||
@@ -406,7 +408,12 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
case 1:
|
case 1:
|
||||||
bf.WriteUint8(0)
|
bf.WriteUint8(0)
|
||||||
case 2:
|
case 2:
|
||||||
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%dname=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), pkt.Name, s.charID)
|
switch pkt.BoxType {
|
||||||
|
case 0:
|
||||||
|
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET item%dname=$1 WHERE character_id=$2", pkt.BoxIndex), pkt.Name, s.charID)
|
||||||
|
case 1:
|
||||||
|
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET equip%dname=$1 WHERE character_id=$2", pkt.BoxIndex), pkt.Name, s.charID)
|
||||||
|
}
|
||||||
case 3:
|
case 3:
|
||||||
bf.WriteUint32(0) // Usage renewal time, >1 = disabled
|
bf.WriteUint32(0) // Usage renewal time, >1 = disabled
|
||||||
bf.WriteUint16(10000) // Usages
|
bf.WriteUint16(10000) // Usages
|
||||||
@@ -424,81 +431,63 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
func addWarehouseGift(s *Session, boxType string, giftStack mhfpacket.WarehouseStack) {
|
func addWarehouseItem(s *Session, item mhfitem.MHFItemStack) {
|
||||||
giftBox := getWarehouseBox(s, boxType, 10)
|
giftBox := warehouseGetItems(s, 10)
|
||||||
if boxType == "item" {
|
item.WarehouseID = token.RNG.Uint32()
|
||||||
exists := false
|
giftBox = append(giftBox, item)
|
||||||
for i, stack := range giftBox {
|
s.server.db.Exec("UPDATE warehouse SET item10=$1 WHERE character_id=$2", mhfitem.SerializeWarehouseItems(giftBox), s.charID)
|
||||||
if stack.ItemID == giftStack.ItemID {
|
|
||||||
exists = true
|
|
||||||
giftBox[i].Quantity += giftStack.Quantity
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if exists == false {
|
|
||||||
giftBox = append(giftBox, giftStack)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
giftBox = append(giftBox, giftStack)
|
|
||||||
}
|
|
||||||
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s10=$1 WHERE character_id=$2", boxType), boxToBytes(giftBox, boxType), s.charID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getWarehouseBox(s *Session, boxType string, boxIndex uint8) []mhfpacket.WarehouseStack {
|
func addWarehouseEquipment(s *Session, equipment mhfitem.MHFEquipment) {
|
||||||
|
giftBox := warehouseGetEquipment(s, 10)
|
||||||
|
equipment.WarehouseID = token.RNG.Uint32()
|
||||||
|
giftBox = append(giftBox, equipment)
|
||||||
|
s.server.db.Exec("UPDATE warehouse SET equip10=$1 WHERE character_id=$2", mhfitem.SerializeWarehouseEquipment(giftBox), s.charID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func warehouseGetItems(s *Session, index uint8) []mhfitem.MHFItemStack {
|
||||||
var data []byte
|
var data []byte
|
||||||
s.server.db.QueryRow(fmt.Sprintf("SELECT %s%d FROM warehouse WHERE character_id=$1", boxType, boxIndex), s.charID).Scan(&data)
|
var items []mhfitem.MHFItemStack
|
||||||
|
s.server.db.QueryRow(fmt.Sprintf(`SELECT item%d FROM warehouse WHERE character_id=$1`, index), s.charID).Scan(&data)
|
||||||
if len(data) > 0 {
|
if len(data) > 0 {
|
||||||
box := byteframe.NewByteFrameFromBytes(data)
|
box := byteframe.NewByteFrameFromBytes(data)
|
||||||
numStacks := box.ReadUint16()
|
numStacks := box.ReadUint16()
|
||||||
stacks := make([]mhfpacket.WarehouseStack, numStacks)
|
box.ReadUint16() // Unused
|
||||||
for i := 0; i < int(numStacks); i++ {
|
for i := 0; i < int(numStacks); i++ {
|
||||||
if boxType == "item" {
|
items = append(items, mhfitem.ReadWarehouseItem(box))
|
||||||
stacks[i].ID = box.ReadUint32()
|
|
||||||
stacks[i].Index = box.ReadUint16()
|
|
||||||
stacks[i].ItemID = box.ReadUint16()
|
|
||||||
stacks[i].Quantity = box.ReadUint16()
|
|
||||||
box.ReadUint16()
|
|
||||||
} else {
|
|
||||||
stacks[i].ID = box.ReadUint32()
|
|
||||||
stacks[i].Index = box.ReadUint16()
|
|
||||||
stacks[i].EquipType = box.ReadUint16()
|
|
||||||
stacks[i].ItemID = box.ReadUint16()
|
|
||||||
stacks[i].Data = box.ReadBytes(56)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return stacks
|
return items
|
||||||
} else {
|
|
||||||
return make([]mhfpacket.WarehouseStack, 0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func boxToBytes(stacks []mhfpacket.WarehouseStack, boxType string) []byte {
|
func warehouseGetEquipment(s *Session, index uint8) []mhfitem.MHFEquipment {
|
||||||
bf := byteframe.NewByteFrame()
|
var data []byte
|
||||||
bf.WriteUint16(uint16(len(stacks)))
|
var equipment []mhfitem.MHFEquipment
|
||||||
for i, stack := range stacks {
|
s.server.db.QueryRow(fmt.Sprintf(`SELECT equip%d FROM warehouse WHERE character_id=$1`, index), s.charID).Scan(&data)
|
||||||
if boxType == "item" {
|
if len(data) > 0 {
|
||||||
bf.WriteUint32(stack.ID)
|
box := byteframe.NewByteFrameFromBytes(data)
|
||||||
bf.WriteUint16(uint16(i + 1))
|
numStacks := box.ReadUint16()
|
||||||
bf.WriteUint16(stack.ItemID)
|
box.ReadUint16() // Unused
|
||||||
bf.WriteUint16(stack.Quantity)
|
for i := 0; i < int(numStacks); i++ {
|
||||||
bf.WriteUint16(0)
|
equipment = append(equipment, mhfitem.ReadWarehouseEquipment(box))
|
||||||
} else {
|
|
||||||
bf.WriteUint32(stack.ID)
|
|
||||||
bf.WriteUint16(uint16(i + 1))
|
|
||||||
bf.WriteUint16(stack.EquipType)
|
|
||||||
bf.WriteUint16(stack.ItemID)
|
|
||||||
bf.WriteBytes(stack.Data)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bf.WriteUint16(0)
|
return equipment
|
||||||
return bf.Data()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfEnumerateWarehouse)
|
pkt := p.(*mhfpacket.MsgMhfEnumerateWarehouse)
|
||||||
box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex)
|
bf := byteframe.NewByteFrame()
|
||||||
if len(box) > 0 {
|
switch pkt.BoxType {
|
||||||
doAckBufSucceed(s, pkt.AckHandle, boxToBytes(box, pkt.BoxType))
|
case 0:
|
||||||
|
items := warehouseGetItems(s, pkt.BoxIndex)
|
||||||
|
bf.WriteBytes(mhfitem.SerializeWarehouseItems(items))
|
||||||
|
case 1:
|
||||||
|
equipment := warehouseGetEquipment(s, pkt.BoxIndex)
|
||||||
|
bf.WriteBytes(mhfitem.SerializeWarehouseEquipment(equipment))
|
||||||
|
}
|
||||||
|
if bf.Index() > 0 {
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
} else {
|
} else {
|
||||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
}
|
}
|
||||||
@@ -506,49 +495,34 @@ func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfUpdateWarehouse)
|
pkt := p.(*mhfpacket.MsgMhfUpdateWarehouse)
|
||||||
box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex)
|
switch pkt.BoxType {
|
||||||
// Update existing stacks
|
case 0:
|
||||||
var newStacks []mhfpacket.WarehouseStack
|
newStacks := mhfitem.DiffItemStacks(warehouseGetItems(s, pkt.BoxIndex), pkt.UpdatedItems)
|
||||||
for _, update := range pkt.Updates {
|
s.server.db.Exec(fmt.Sprintf(`UPDATE warehouse SET item%d=$1 WHERE character_id=$2`, pkt.BoxIndex), mhfitem.SerializeWarehouseItems(newStacks), s.charID)
|
||||||
|
case 1:
|
||||||
|
var fEquip []mhfitem.MHFEquipment
|
||||||
|
oEquips := warehouseGetEquipment(s, pkt.BoxIndex)
|
||||||
|
for _, uEquip := range pkt.UpdatedEquipment {
|
||||||
exists := false
|
exists := false
|
||||||
if pkt.BoxType == "item" {
|
for i := range oEquips {
|
||||||
for i, stack := range box {
|
if oEquips[i].WarehouseID == uEquip.WarehouseID {
|
||||||
if stack.Index == update.Index {
|
|
||||||
exists = true
|
exists = true
|
||||||
box[i].Quantity = update.Quantity
|
// Will set removed items to 0
|
||||||
|
oEquips[i].ItemID = uEquip.ItemID
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
if !exists {
|
||||||
for i, stack := range box {
|
uEquip.WarehouseID = token.RNG.Uint32()
|
||||||
if stack.Index == update.Index {
|
fEquip = append(fEquip, uEquip)
|
||||||
exists = true
|
|
||||||
box[i].ItemID = update.ItemID
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
for _, oEquip := range oEquips {
|
||||||
if exists == false {
|
if oEquip.ItemID > 0 {
|
||||||
newStacks = append(newStacks, update)
|
fEquip = append(fEquip, oEquip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Append new stacks
|
s.server.db.Exec(fmt.Sprintf(`UPDATE warehouse SET equip%d=$1 WHERE character_id=$2`, pkt.BoxIndex), mhfitem.SerializeWarehouseEquipment(fEquip), s.charID)
|
||||||
for _, stack := range newStacks {
|
|
||||||
box = append(box, stack)
|
|
||||||
}
|
}
|
||||||
// Slice empty stacks
|
|
||||||
var cleanedBox []mhfpacket.WarehouseStack
|
|
||||||
for _, stack := range box {
|
|
||||||
if pkt.BoxType == "item" {
|
|
||||||
if stack.Quantity > 0 {
|
|
||||||
cleanedBox = append(cleanedBox, stack)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if stack.ItemID != 0 {
|
|
||||||
cleanedBox = append(cleanedBox, stack)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%d=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), boxToBytes(cleanedBox, pkt.BoxType), s.charID)
|
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ func makeEventQuest(s *Session, rows *sql.Rows) ([]byte, error) {
|
|||||||
bf.WriteBool(true)
|
bf.WriteBool(true)
|
||||||
}
|
}
|
||||||
bf.WriteUint16(0) // Unk
|
bf.WriteUint16(0) // Unk
|
||||||
if _config.ErupeConfig.RealClientMode >= _config.G1 {
|
if _config.ErupeConfig.RealClientMode >= _config.G2 {
|
||||||
bf.WriteUint32(mark)
|
bf.WriteUint32(mark)
|
||||||
}
|
}
|
||||||
bf.WriteUint16(0) // Unk
|
bf.WriteUint16(0) // Unk
|
||||||
@@ -578,7 +578,7 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
tuneValues = temp
|
tuneValues = temp
|
||||||
|
|
||||||
tuneLimit := 770
|
tuneLimit := 770
|
||||||
if _config.ErupeConfig.RealClientMode <= _config.F5 {
|
if _config.ErupeConfig.RealClientMode <= _config.G1 {
|
||||||
tuneLimit = 256
|
tuneLimit = 256
|
||||||
} else if _config.ErupeConfig.RealClientMode <= _config.G3 {
|
} else if _config.ErupeConfig.RealClientMode <= _config.G3 {
|
||||||
tuneLimit = 283
|
tuneLimit = 283
|
||||||
|
|||||||
@@ -148,9 +148,24 @@ func removeSessionFromStage(s *Session) {
|
|||||||
destructEmptySemaphores(s)
|
destructEmptySemaphores(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isStageFull(s *Session, StageID string) bool {
|
||||||
|
if stage, exists := s.server.stages[StageID]; exists {
|
||||||
|
if _, exists := stage.reservedClientSlots[s.charID]; exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return len(stage.reservedClientSlots)+len(stage.clients) >= int(stage.maxPlayers)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgSysEnterStage)
|
pkt := p.(*mhfpacket.MsgSysEnterStage)
|
||||||
|
|
||||||
|
if isStageFull(s, pkt.StageID) {
|
||||||
|
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Push our current stage ID to the movement stack before entering another one.
|
// Push our current stage ID to the movement stack before entering another one.
|
||||||
if s.stage != nil {
|
if s.stage != nil {
|
||||||
s.stage.Lock()
|
s.stage.Lock()
|
||||||
@@ -175,6 +190,12 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
backStage = "sl1Ns200p0a0u0"
|
backStage = "sl1Ns200p0a0u0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isStageFull(s, backStage) {
|
||||||
|
s.stageMoveStack.Push(backStage)
|
||||||
|
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if _, exists := s.stage.reservedClientSlots[s.charID]; exists {
|
if _, exists := s.stage.reservedClientSlots[s.charID]; exists {
|
||||||
delete(s.stage.reservedClientSlots, s.charID)
|
delete(s.stage.reservedClientSlots, s.charID)
|
||||||
}
|
}
|
||||||
@@ -188,6 +209,12 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
func handleMsgSysMoveStage(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysMoveStage(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgSysMoveStage)
|
pkt := p.(*mhfpacket.MsgSysMoveStage)
|
||||||
|
|
||||||
|
if isStageFull(s, pkt.StageID) {
|
||||||
|
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
doStageTransfer(s, pkt.AckHandle, pkt.StageID)
|
doStageTransfer(s, pkt.AckHandle, pkt.StageID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -367,6 +367,14 @@ func (s *Server) DiscordChannelSend(charName string, content string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) DiscordScreenShotSend(charName string, title string, description string, articleToken string) {
|
||||||
|
if s.erupeConfig.Discord.Enabled && s.discordBot != nil {
|
||||||
|
imageUrl := fmt.Sprintf("%s:%d/api/ss/bbs/%s", s.erupeConfig.Screenshots.Host, s.erupeConfig.Screenshots.Port, articleToken)
|
||||||
|
message := fmt.Sprintf("**%s**: %s - %s %s", charName, title, description, imageUrl)
|
||||||
|
s.discordBot.RealtimeChannelSend(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) FindSessionByCharID(charID uint32) *Session {
|
func (s *Server) FindSessionByCharID(charID uint32) *Session {
|
||||||
for _, c := range s.Channels {
|
for _, c := range s.Channels {
|
||||||
for _, session := range c.sessions {
|
for _, session := range c.sessions {
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func NewStage(ID string) *Stage {
|
|||||||
objects: make(map[uint32]*Object),
|
objects: make(map[uint32]*Object),
|
||||||
objectIndex: 0,
|
objectIndex: 0,
|
||||||
rawBinaryData: make(map[stageBinaryKey][]byte),
|
rawBinaryData: make(map[stageBinaryKey][]byte),
|
||||||
maxPlayers: 4,
|
maxPlayers: 127,
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package discordbot
|
package discordbot
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"erupe-ce/config"
|
_config "erupe-ce/config"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/bwmarrin/discordgo"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"regexp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var Commands = []*discordgo.ApplicationCommand{
|
var Commands = []*discordgo.ApplicationCommand{
|
||||||
@@ -113,7 +114,6 @@ func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReplaceTextAll(text string, regex *regexp.Regexp, handler func(input string) string) string {
|
func ReplaceTextAll(text string, regex *regexp.Regexp, handler func(input string) string) string {
|
||||||
result := regex.ReplaceAllFunc([]byte(text), func(s []byte) []byte {
|
result := regex.ReplaceAllFunc([]byte(text), func(s []byte) []byte {
|
||||||
input := regex.ReplaceAllString(string(s), `$1`)
|
input := regex.ReplaceAllString(string(s), `$1`)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func (s *Server) newUserChara(uid uint32) error {
|
|||||||
_, err = s.db.Exec(`
|
_, err = s.db.Exec(`
|
||||||
INSERT INTO characters (
|
INSERT INTO characters (
|
||||||
user_id, is_female, is_new_character, name, unk_desc_string,
|
user_id, is_female, is_new_character, name, unk_desc_string,
|
||||||
hrp, gr, weapon_type, last_login)
|
hr, gr, weapon_type, last_login)
|
||||||
VALUES($1, False, True, '', '', 0, 0, 0, $2)`,
|
VALUES($1, False, True, '', '', 0, 0, 0, $2)`,
|
||||||
uid,
|
uid,
|
||||||
uint32(time.Now().Unix()),
|
uint32(time.Now().Unix()),
|
||||||
@@ -63,7 +63,7 @@ type character struct {
|
|||||||
IsNewCharacter bool `db:"is_new_character"`
|
IsNewCharacter bool `db:"is_new_character"`
|
||||||
Name string `db:"name"`
|
Name string `db:"name"`
|
||||||
UnkDescString string `db:"unk_desc_string"`
|
UnkDescString string `db:"unk_desc_string"`
|
||||||
HRP uint16 `db:"hrp"`
|
HR uint16 `db:"hr"`
|
||||||
GR uint16 `db:"gr"`
|
GR uint16 `db:"gr"`
|
||||||
WeaponType uint16 `db:"weapon_type"`
|
WeaponType uint16 `db:"weapon_type"`
|
||||||
LastLogin uint32 `db:"last_login"`
|
LastLogin uint32 `db:"last_login"`
|
||||||
@@ -71,7 +71,7 @@ type character struct {
|
|||||||
|
|
||||||
func (s *Server) getCharactersForUser(uid uint32) ([]character, error) {
|
func (s *Server) getCharactersForUser(uid uint32) ([]character, error) {
|
||||||
characters := make([]character, 0)
|
characters := make([]character, 0)
|
||||||
err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE user_id = $1 AND deleted = false ORDER BY id", uid)
|
err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hr, gr, weapon_type, last_login FROM characters WHERE user_id = $1 AND deleted = false ORDER BY id", uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ import (
|
|||||||
_config "erupe-ce/config"
|
_config "erupe-ce/config"
|
||||||
"erupe-ce/server/channelserver"
|
"erupe-ce/server/channelserver"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go.uber.org/zap"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Session) makeSignResponse(uid uint32) []byte {
|
func (s *Session) makeSignResponse(uid uint32) []byte {
|
||||||
@@ -38,26 +39,25 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
|
|||||||
return bf.Data()
|
return bf.Data()
|
||||||
}
|
}
|
||||||
|
|
||||||
bf.WriteUint8(uint8(SIGN_SUCCESS)) // resp_code
|
if s.client == PS3 && (s.server.erupeConfig.PatchServerFile == "" || s.server.erupeConfig.PatchServerManifest == "") {
|
||||||
if (s.server.erupeConfig.PatchServerManifest != "" && s.server.erupeConfig.PatchServerFile != "") || s.client == PS3 {
|
bf.WriteUint8(uint8(SIGN_EABORT))
|
||||||
bf.WriteUint8(2)
|
return bf.Data()
|
||||||
} else {
|
|
||||||
bf.WriteUint8(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bf.WriteUint8(uint8(SIGN_SUCCESS))
|
||||||
|
bf.WriteUint8(2) // patch server count
|
||||||
bf.WriteUint8(1) // entrance server count
|
bf.WriteUint8(1) // entrance server count
|
||||||
bf.WriteUint8(uint8(len(chars)))
|
bf.WriteUint8(uint8(len(chars)))
|
||||||
bf.WriteUint32(tokenID)
|
bf.WriteUint32(tokenID)
|
||||||
bf.WriteBytes([]byte(sessToken))
|
bf.WriteBytes([]byte(sessToken))
|
||||||
bf.WriteUint32(uint32(channelserver.TimeAdjusted().Unix()))
|
bf.WriteUint32(uint32(channelserver.TimeAdjusted().Unix()))
|
||||||
if s.client == PS3 {
|
if s.client == PS3 {
|
||||||
ps.Uint8(bf, fmt.Sprintf(`ps3-%s.zerulight.cc`, s.server.erupeConfig.Language), false)
|
ps.Uint8(bf, fmt.Sprintf("%s/ps3", s.server.erupeConfig.PatchServerManifest), false)
|
||||||
ps.Uint8(bf, fmt.Sprintf(`ps3-%s.zerulight.cc`, s.server.erupeConfig.Language), false)
|
ps.Uint8(bf, fmt.Sprintf("%s/ps3", s.server.erupeConfig.PatchServerFile), false)
|
||||||
} else {
|
} else {
|
||||||
if s.server.erupeConfig.PatchServerManifest != "" && s.server.erupeConfig.PatchServerFile != "" {
|
|
||||||
ps.Uint8(bf, s.server.erupeConfig.PatchServerManifest, false)
|
ps.Uint8(bf, s.server.erupeConfig.PatchServerManifest, false)
|
||||||
ps.Uint8(bf, s.server.erupeConfig.PatchServerFile, false)
|
ps.Uint8(bf, s.server.erupeConfig.PatchServerFile, false)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if strings.Split(s.rawConn.RemoteAddr().String(), ":")[0] == "127.0.0.1" {
|
if strings.Split(s.rawConn.RemoteAddr().String(), ":")[0] == "127.0.0.1" {
|
||||||
ps.Uint8(bf, fmt.Sprintf("127.0.0.1:%d", s.server.erupeConfig.Entrance.Port), false)
|
ps.Uint8(bf, fmt.Sprintf("127.0.0.1:%d", s.server.erupeConfig.Entrance.Port), false)
|
||||||
} else {
|
} else {
|
||||||
@@ -70,14 +70,11 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
|
|||||||
lastPlayed = char.ID
|
lastPlayed = char.ID
|
||||||
}
|
}
|
||||||
bf.WriteUint32(char.ID)
|
bf.WriteUint32(char.ID)
|
||||||
|
|
||||||
// Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999
|
|
||||||
if s.server.erupeConfig.DebugOptions.MaxLauncherHR {
|
if s.server.erupeConfig.DebugOptions.MaxLauncherHR {
|
||||||
bf.WriteUint16(999)
|
bf.WriteUint16(999)
|
||||||
} else {
|
} else {
|
||||||
bf.WriteUint16(char.HRP)
|
bf.WriteUint16(char.HR)
|
||||||
}
|
}
|
||||||
|
|
||||||
bf.WriteUint16(char.WeaponType) // Weapon, 0-13.
|
bf.WriteUint16(char.WeaponType) // Weapon, 0-13.
|
||||||
bf.WriteUint32(char.LastLogin) // Last login date, unix timestamp in seconds.
|
bf.WriteUint32(char.LastLogin) // Last login date, unix timestamp in seconds.
|
||||||
bf.WriteBool(char.IsFemale) // Sex, 0=male, 1=female.
|
bf.WriteBool(char.IsFemale) // Sex, 0=male, 1=female.
|
||||||
@@ -139,7 +136,7 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
|
|||||||
bf.WriteUint32(s.server.getLastCID(uid))
|
bf.WriteUint32(s.server.getLastCID(uid))
|
||||||
bf.WriteUint32(s.server.getUserRights(uid))
|
bf.WriteUint32(s.server.getUserRights(uid))
|
||||||
ps.Uint16(bf, "", false) // filters
|
ps.Uint16(bf, "", false) // filters
|
||||||
if s.client == VITA || s.client == PS3 {
|
if s.client == VITA || s.client == PS3 || s.client == PS4 {
|
||||||
var psnUser string
|
var psnUser string
|
||||||
s.server.db.QueryRow("SELECT psn_id FROM users WHERE id = $1", uid).Scan(&psnUser)
|
s.server.db.QueryRow("SELECT psn_id FROM users WHERE id = $1", uid).Scan(&psnUser)
|
||||||
bf.WriteBytes(stringsupport.PaddedString(psnUser, 20, true))
|
bf.WriteBytes(stringsupport.PaddedString(psnUser, 20, true))
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
"erupe-ce/network"
|
"erupe-ce/network"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ const (
|
|||||||
PC100 client = iota
|
PC100 client = iota
|
||||||
VITA
|
VITA
|
||||||
PS3
|
PS3
|
||||||
|
PS4
|
||||||
WIIU
|
WIIU
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -56,6 +58,9 @@ func (s *Session) handlePacket(pkt []byte) error {
|
|||||||
switch reqType[:len(reqType)-3] {
|
switch reqType[:len(reqType)-3] {
|
||||||
case "DLTSKEYSIGN:", "DSGN:", "SIGN:":
|
case "DLTSKEYSIGN:", "DSGN:", "SIGN:":
|
||||||
s.handleDSGN(bf)
|
s.handleDSGN(bf)
|
||||||
|
case "PS4SGN:":
|
||||||
|
s.client = PS4
|
||||||
|
s.handlePSSGN(bf)
|
||||||
case "PS3SGN:":
|
case "PS3SGN:":
|
||||||
s.client = PS3
|
s.client = PS3
|
||||||
s.handlePSSGN(bf)
|
s.handlePSSGN(bf)
|
||||||
@@ -127,13 +132,16 @@ func (s *Session) handleWIIUSGN(bf *byteframe.ByteFrame) {
|
|||||||
|
|
||||||
func (s *Session) handlePSSGN(bf *byteframe.ByteFrame) {
|
func (s *Session) handlePSSGN(bf *byteframe.ByteFrame) {
|
||||||
// Prevent reading malformed request
|
// Prevent reading malformed request
|
||||||
|
if s.client != PS4 {
|
||||||
if len(bf.DataFromCurrent()) < 128 {
|
if len(bf.DataFromCurrent()) < 128 {
|
||||||
s.sendCode(SIGN_EABORT)
|
s.sendCode(SIGN_EABORT)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = bf.ReadNullTerminatedBytes() // VITA = 0000000256, PS3 = 0000000255
|
_ = bf.ReadNullTerminatedBytes() // VITA = 0000000256, PS3 = 0000000255
|
||||||
_ = bf.ReadBytes(2) // VITA = 1, PS3 = !
|
_ = bf.ReadBytes(2) // VITA = 1, PS3 = !
|
||||||
_ = bf.ReadBytes(82)
|
_ = bf.ReadBytes(82)
|
||||||
|
}
|
||||||
s.psn = string(bf.ReadNullTerminatedBytes())
|
s.psn = string(bf.ReadNullTerminatedBytes())
|
||||||
var uid uint32
|
var uid uint32
|
||||||
err := s.server.db.QueryRow(`SELECT id FROM users WHERE psn_id = $1`, s.psn).Scan(&uid)
|
err := s.server.db.QueryRow(`SELECT id FROM users WHERE psn_id = $1`, s.psn).Scan(&uid)
|
||||||
|
|||||||
Reference in New Issue
Block a user