mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-13 15:34:38 +01:00
Merge branch 'refs/heads/main' into fix/quest-stamps
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/*
|
||||||
@@ -29,6 +29,23 @@ func SJISToUTF8(b []byte) string {
|
|||||||
return string(result)
|
return string(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ToNGWord(x string) []uint16 {
|
||||||
|
var w []uint16
|
||||||
|
for _, r := range []rune(x) {
|
||||||
|
if r > 0xFF {
|
||||||
|
t := UTF8ToSJIS(string(r))
|
||||||
|
if len(t) > 1 {
|
||||||
|
w = append(w, uint16(t[1])<<8|uint16(t[0]))
|
||||||
|
} else {
|
||||||
|
w = append(w, uint16(t[0]))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
w = append(w, uint16(r))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
func PaddedString(x string, size uint, t bool) []byte {
|
func PaddedString(x string, size uint, t bool) []byte {
|
||||||
if t {
|
if t {
|
||||||
e := japanese.ShiftJIS.NewEncoder()
|
e := japanese.ShiftJIS.NewEncoder()
|
||||||
|
|||||||
12
config.json
12
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,
|
||||||
@@ -189,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,16 +86,18 @@ type Config struct {
|
|||||||
EarthID int32
|
EarthID int32
|
||||||
EarthMonsters []int32
|
EarthMonsters []int32
|
||||||
SaveDumps SaveDumpOptions
|
SaveDumps SaveDumpOptions
|
||||||
DebugOptions DebugOptions
|
Screenshots ScreenshotsOptions
|
||||||
GameplayOptions GameplayOptions
|
|
||||||
Discord Discord
|
DebugOptions DebugOptions
|
||||||
Commands []Command
|
GameplayOptions GameplayOptions
|
||||||
Courses []Course
|
Discord Discord
|
||||||
Database Database
|
Commands []Command
|
||||||
Sign Sign
|
Courses []Course
|
||||||
SignV2 SignV2
|
Database Database
|
||||||
Channel Channel
|
Sign Sign
|
||||||
Entrance Entrance
|
API API
|
||||||
|
Channel Channel
|
||||||
|
Entrance Entrance
|
||||||
}
|
}
|
||||||
|
|
||||||
type SaveDumpOptions struct {
|
type SaveDumpOptions struct {
|
||||||
@@ -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.
|
||||||
@@ -228,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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ func (m *MsgSysTerminalLog) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client
|
|||||||
entryCount := int(bf.ReadUint16())
|
entryCount := int(bf.ReadUint16())
|
||||||
bf.ReadUint16() // Zeroed
|
bf.ReadUint16() // Zeroed
|
||||||
|
|
||||||
var e TerminalLogEntry
|
|
||||||
for i := 0; i < entryCount; i++ {
|
for i := 0; i < entryCount; i++ {
|
||||||
|
var e TerminalLogEntry
|
||||||
e.Index = bf.ReadUint32()
|
e.Index = bf.ReadUint32()
|
||||||
e.Type1 = bf.ReadUint8()
|
e.Type1 = bf.ReadUint8()
|
||||||
e.Type2 = bf.ReadUint8()
|
e.Type2 = bf.ReadUint8()
|
||||||
|
|||||||
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,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,7 +53,7 @@ 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, hr, 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",
|
||||||
@@ -78,7 +78,7 @@ func (s *Server) createCharacter(ctx context.Context, userID uint32) (Character,
|
|||||||
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,7 +92,7 @@ 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, `
|
||||||
@@ -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 {
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -852,26 +852,29 @@ 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())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -879,7 +882,7 @@ 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 mhfitem.MHFItemStack
|
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 = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 2210}, Quantity: 1}
|
tktStack = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 2210}, Quantity: 1}
|
||||||
} else {
|
} else {
|
||||||
@@ -895,7 +898,8 @@ func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
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())
|
||||||
}
|
}
|
||||||
@@ -1083,6 +1087,31 @@ func getStampcardReward(secondStamp bool, HR uint16, GR uint16) mhfitem.MHFItemS
|
|||||||
|
|
||||||
func handleMsgMhfStampcardStamp(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfStampcardStamp(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfStampcardStamp)
|
pkt := p.(*mhfpacket.MsgMhfStampcardStamp)
|
||||||
|
|
||||||
|
rewards := []struct {
|
||||||
|
HR uint16
|
||||||
|
Item1 uint16
|
||||||
|
Quantity1 uint16
|
||||||
|
Item2 uint16
|
||||||
|
Quantity2 uint16
|
||||||
|
}{
|
||||||
|
{0, 6164, 1, 6164, 2},
|
||||||
|
{50, 6164, 2, 6164, 3},
|
||||||
|
{100, 6164, 3, 5392, 1},
|
||||||
|
{300, 5392, 1, 5392, 3},
|
||||||
|
{999, 5392, 1, 5392, 4},
|
||||||
|
}
|
||||||
|
if _config.ErupeConfig.RealClientMode <= _config.Z1 {
|
||||||
|
for _, reward := range rewards {
|
||||||
|
if pkt.HR >= reward.HR {
|
||||||
|
pkt.Item1 = reward.Item1
|
||||||
|
pkt.Quantity1 = reward.Quantity1
|
||||||
|
pkt.Item2 = reward.Item2
|
||||||
|
pkt.Quantity2 = reward.Quantity2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
bf.WriteUint16(pkt.HR)
|
bf.WriteUint16(pkt.HR)
|
||||||
if _config.ErupeConfig.RealClientMode >= _config.G1 {
|
if _config.ErupeConfig.RealClientMode >= _config.G1 {
|
||||||
|
|||||||
@@ -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))
|
||||||
|
//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())
|
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)
|
||||||
bf.WriteUint16(0)
|
if _config.ErupeConfig.RealClientMode >= _config.ZZ {
|
||||||
ps.Uint16(bf, fmt.Sprintf(s.server.i18n.cafe.reset, int(cafeReset.Month()), cafeReset.Day()), true)
|
bf.WriteUint16(0)
|
||||||
|
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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -367,13 +367,17 @@ func handleMsgMhfAcquireTitle(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {
|
func initializeWarehouse(s *Session) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfOperateWarehouse)
|
|
||||||
var t int
|
var t int
|
||||||
err := s.server.db.QueryRow("SELECT character_id FROM warehouse WHERE character_id=$1", s.charID).Scan(&t)
|
err := s.server.db.QueryRow("SELECT character_id FROM warehouse WHERE character_id=$1", s.charID).Scan(&t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.server.db.Exec("INSERT INTO warehouse (character_id) VALUES ($1)", s.charID)
|
s.server.db.Exec("INSERT INTO warehouse (character_id) VALUES ($1)", s.charID)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfOperateWarehouse)
|
||||||
|
initializeWarehouse(s)
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
bf.WriteUint8(pkt.Operation)
|
bf.WriteUint8(pkt.Operation)
|
||||||
switch pkt.Operation {
|
switch pkt.Operation {
|
||||||
@@ -446,6 +450,7 @@ func addWarehouseEquipment(s *Session, equipment mhfitem.MHFEquipment) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func warehouseGetItems(s *Session, index uint8) []mhfitem.MHFItemStack {
|
func warehouseGetItems(s *Session, index uint8) []mhfitem.MHFItemStack {
|
||||||
|
initializeWarehouse(s)
|
||||||
var data []byte
|
var data []byte
|
||||||
var items []mhfitem.MHFItemStack
|
var items []mhfitem.MHFItemStack
|
||||||
s.server.db.QueryRow(fmt.Sprintf(`SELECT item%d FROM warehouse WHERE character_id=$1`, index), s.charID).Scan(&data)
|
s.server.db.QueryRow(fmt.Sprintf(`SELECT item%d FROM warehouse WHERE character_id=$1`, index), s.charID).Scan(&data)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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`)
|
||||||
|
|||||||
@@ -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 {
|
||||||
@@ -134,8 +135,204 @@ 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
|
|
||||||
if s.client == VITA || s.client == PS3 {
|
namNGWords := []string{}
|
||||||
|
msgNGWords := []string{}
|
||||||
|
|
||||||
|
filters := byteframe.NewByteFrame()
|
||||||
|
filters.SetLE()
|
||||||
|
filters.WriteNullTerminatedBytes([]byte("smc"))
|
||||||
|
smc := byteframe.NewByteFrame()
|
||||||
|
smc.SetLE()
|
||||||
|
smcData := []struct {
|
||||||
|
charGroup [][]rune
|
||||||
|
}{
|
||||||
|
{[][]rune{{'='}, {'='}}},
|
||||||
|
{[][]rune{{')'}, {')'}}},
|
||||||
|
{[][]rune{{'('}, {'('}}},
|
||||||
|
{[][]rune{{'!'}, {'!'}}},
|
||||||
|
{[][]rune{{'/'}, {'/'}}},
|
||||||
|
{[][]rune{{'+'}, {'+'}}},
|
||||||
|
{[][]rune{{'&'}, {'&'}}},
|
||||||
|
{[][]rune{{'ぼ'}, {'ボ'}, {'ホ', '゙'}, {'ほ', '゙'}, {'ホ', '゙'}, {'ほ', '゛'}, {'ホ', '゛'}, {'ホ', '゛'}}},
|
||||||
|
{[][]rune{{'べ'}, {'ベ'}, {'ヘ', '゙'}, {'へ', '゙'}, {'ヘ', '゙'}, {'へ', '゛'}, {'ヘ', '゛'}, {'ヘ', '゛'}}},
|
||||||
|
{[][]rune{{'で'}, {'デ'}, {'テ', '゙'}, {'て', '゙'}, {'テ', '゙'}, {'て', '゛'}, {'テ', '゛'}, {'テ', '゛'}, {'〒', '゛'}, {'〒', '゙'}, {'乙', '゙'}, {'乙', '゛'}}},
|
||||||
|
{[][]rune{{'び'}, {'ビ'}, {'ヒ', '゙'}, {'ひ', '゙'}, {'ヒ', '゙'}, {'ひ', '゛'}, {'ヒ', '゛'}, {'ヒ', '゛'}}},
|
||||||
|
{[][]rune{{'ど'}, {'ド'}, {'ト', '゙'}, {'と', '゙'}, {'ト', '゙'}, {'と', '゛'}, {'ト', '゛'}, {'ト', '゛'}, {'┣', '゙'}, {'┣', '゛'}, {'├', '゙'}, {'├', '゛'}}},
|
||||||
|
{[][]rune{{'ば'}, {'バ'}, {'ハ', '゙'}, {'は', '゙'}, {'ハ', '゙'}, {'八', '゙'}, {'は', '゛'}, {'ハ', '゛'}, {'ハ', '゛'}, {'八', '゛'}}},
|
||||||
|
{[][]rune{{'つ', '゙'}, {'ヅ'}, {'ツ', '゙'}, {'つ', '゛'}, {'ツ', '゛'}, {'ツ', '゙'}, {'ツ', '゛'}, {'づ'}, {'っ', '゙'}, {'ッ', '゙'}, {'ッ', '゙'}, {'っ', '゛'}, {'ッ', '゛'}, {'ッ', '゛'}}},
|
||||||
|
{[][]rune{{'ぶ'}, {'ブ'}, {'フ', '゙'}, {'ヴ'}, {'ウ', '゙'}, {'う', '゛'}, {'う', '゙'}, {'ウ', '゙'}, {'ゥ', '゙'}, {'ぅ', '゙'}, {'ふ', '゙'}, {'フ', '゙'}, {'フ', '゛'}}},
|
||||||
|
{[][]rune{{'ぢ'}, {'ヂ'}, {'チ', '゙'}, {'ち', '゙'}, {'チ', '゙'}, {'ち', '゛'}, {'チ', '゛'}, {'チ', '゛'}, {'千', '゛'}, {'千', '゙'}}},
|
||||||
|
{[][]rune{{'だ'}, {'ダ'}, {'タ', '゙'}, {'た', '゙'}, {'タ', '゙'}, {'夕', '゙'}, {'た', '゛'}, {'タ', '゛'}, {'タ', '゛'}, {'夕', '゛'}}},
|
||||||
|
{[][]rune{{'ぞ'}, {'ゾ'}, {'ソ', '゙'}, {'そ', '゙'}, {'ソ', '゙'}, {'そ', '゛'}, {'ソ', '゛'}, {'ソ', '゛'}, {'ン', '゙'}, {'ン', '゛'}, {'ン', '゛'}, {'ン', '゙'}, {'リ', '゙'}, {'リ', '゙'}, {'リ', '゛'}, {'リ', '゛'}}},
|
||||||
|
{[][]rune{{'ぜ'}, {'セ', '゙'}, {'せ', '゙'}, {'セ', '゙'}, {'せ', '゛'}, {'セ', '゛'}, {'セ', '゛'}, {'ゼ'}}},
|
||||||
|
{[][]rune{{'ず'}, {'ズ'}, {'ス', '゙'}, {'す', '゙'}, {'ス', '゙'}, {'す', '゛'}, {'ス', '゛'}, {'ス', '゛'}}},
|
||||||
|
{[][]rune{{'じ'}, {'ジ'}, {'シ', '゙'}, {'し', '゙'}, {'シ', '゙'}, {'し', '゛'}, {'シ', '゛'}, {'シ', '゛'}}},
|
||||||
|
{[][]rune{{'ざ'}, {'ザ'}, {'サ', '゙'}, {'さ', '゙'}, {'サ', '゙'}, {'さ', '゛'}, {'サ', '゛'}, {'サ', '゛'}}},
|
||||||
|
{[][]rune{{'ご'}, {'ゴ'}, {'コ', '゙'}, {'こ', '゙'}, {'コ', '゙'}, {'こ', '゛'}, {'コ', '゛'}, {'コ', '゛'}}},
|
||||||
|
{[][]rune{{'げ'}, {'ゲ'}, {'ケ', '゙'}, {'け', '゙'}, {'ケ', '゙'}, {'け', '゛'}, {'ケ', '゛'}, {'ケ', '゛'}, {'ヶ', '゙'}, {'ヶ', '゛'}}},
|
||||||
|
{[][]rune{{'ぐ'}, {'グ'}, {'ク', '゙'}, {'く', '゙'}, {'ク', '゙'}, {'く', '゛'}, {'ク', '゛'}, {'ク', '゛'}}},
|
||||||
|
{[][]rune{{'ぎ'}, {'ギ'}, {'キ', '゙'}, {'き', '゙'}, {'キ', '゙'}, {'き', '゛'}, {'キ', '゛'}, {'キ', '゛'}}},
|
||||||
|
{[][]rune{{'が'}, {'ガ'}, {'カ', '゙'}, {'ヵ', '゙'}, {'カ', '゙'}, {'か', '゙'}, {'力', '゙'}, {'ヵ', '゛'}, {'カ', '゛'}, {'か', '゛'}, {'力', '゛'}, {'カ', '゛'}}},
|
||||||
|
{[][]rune{{'を'}, {'ヲ'}, {'ヲ'}}},
|
||||||
|
{[][]rune{{'わ'}, {'ワ'}, {'ワ'}, {'ヮ'}}},
|
||||||
|
{[][]rune{{'ろ'}, {'ロ'}, {'ロ'}, {'□'}, {'口'}}},
|
||||||
|
{[][]rune{{'れ'}, {'レ'}, {'レ'}}},
|
||||||
|
{[][]rune{{'る'}, {'ル'}, {'ル'}}},
|
||||||
|
{[][]rune{{'り'}, {'リ'}, {'リ'}}},
|
||||||
|
{[][]rune{{'ら'}, {'ラ'}, {'ラ'}}},
|
||||||
|
{[][]rune{{'よ'}, {'ヨ'}, {'ヨ'}, {'ョ'}, {'ょ'}, {'ョ'}}},
|
||||||
|
{[][]rune{{'ゆ'}, {'ユ'}, {'ユ'}, {'ュ'}, {'ゅ'}, {'ュ'}}},
|
||||||
|
{[][]rune{{'や'}, {'ヤ'}, {'ヤ'}, {'ャ'}, {'ゃ'}, {'ャ'}}},
|
||||||
|
{[][]rune{{'も'}, {'モ'}, {'モ'}}},
|
||||||
|
{[][]rune{{'め'}, {'メ'}, {'メ'}, {'M', 'E'}}},
|
||||||
|
{[][]rune{{'む'}, {'ム'}, {'ム'}}},
|
||||||
|
{[][]rune{{'み'}, {'ミ'}, {'ミ'}}},
|
||||||
|
{[][]rune{{'ま'}, {'マ'}, {'マ'}}},
|
||||||
|
{[][]rune{{'ほ'}, {'ホ'}, {'ホ'}}},
|
||||||
|
{[][]rune{{'へ'}, {'ヘ'}, {'ヘ'}}},
|
||||||
|
{[][]rune{{'ふ'}, {'フ'}, {'フ'}}},
|
||||||
|
{[][]rune{{'ひ'}, {'ヒ'}, {'ヒ'}}},
|
||||||
|
{[][]rune{{'は'}, {'ハ'}, {'ハ'}, {'八'}}},
|
||||||
|
{[][]rune{{'の'}, {'ノ'}, {'ノ'}}},
|
||||||
|
{[][]rune{{'ね'}, {'ネ'}, {'ネ'}}},
|
||||||
|
{[][]rune{{'ぬ'}, {'ヌ'}, {'ヌ'}}},
|
||||||
|
{[][]rune{{'に'}, {'ニ'}, {'ニ'}, {'二'}}},
|
||||||
|
{[][]rune{{'な'}, {'ナ'}, {'ナ'}}},
|
||||||
|
{[][]rune{{'と'}, {'ト'}, {'ト'}, {'┣'}, {'├'}}},
|
||||||
|
{[][]rune{{'て'}, {'テ'}, {'テ'}, {'〒'}, {'乙'}}},
|
||||||
|
{[][]rune{{'つ'}, {'ツ'}, {'ツ'}, {'っ'}, {'ッ'}, {'ッ'}}},
|
||||||
|
{[][]rune{{'ち'}, {'チ'}, {'チ'}, {'千'}}},
|
||||||
|
{[][]rune{{'た'}, {'タ'}, {'タ'}, {'夕'}}},
|
||||||
|
{[][]rune{{'そ'}, {'ソ'}, {'ソ'}}},
|
||||||
|
{[][]rune{{'せ'}, {'セ'}, {'セ'}}},
|
||||||
|
{[][]rune{{'す'}, {'ス'}, {'ス'}}},
|
||||||
|
{[][]rune{{'し'}, {'シ'}, {'シ'}}},
|
||||||
|
{[][]rune{{'さ'}, {'サ'}, {'サ'}}},
|
||||||
|
{[][]rune{{'こ'}, {'コ'}, {'コ'}}},
|
||||||
|
{[][]rune{{'け'}, {'ケ'}, {'ケ'}, {'ヶ'}}},
|
||||||
|
{[][]rune{{'く'}, {'ク'}, {'ク'}}},
|
||||||
|
{[][]rune{{'き'}, {'キ'}, {'キ'}}},
|
||||||
|
{[][]rune{{'か'}, {'カ'}, {'カ'}, {'ヵ'}, {'力'}}},
|
||||||
|
{[][]rune{{'お'}, {'オ'}, {'オ'}, {'ォ'}, {'ぉ'}, {'ォ'}}},
|
||||||
|
{[][]rune{{'え'}, {'エ'}, {'エ'}, {'ェ'}, {'ぇ'}, {'ェ'}, {'工'}}},
|
||||||
|
{[][]rune{{'う'}, {'ウ'}, {'ウ'}, {'ゥ'}, {'ぅ'}, {'ゥ'}}},
|
||||||
|
{[][]rune{{'い'}, {'イ'}, {'イ'}, {'ィ'}, {'ぃ'}, {'ィ'}}},
|
||||||
|
{[][]rune{{'あ'}, {'ア'}, {'ァ'}, {'ア'}, {'ぁ'}, {'ァ'}}},
|
||||||
|
{[][]rune{{'ー'}, {'―'}, {'‐'}, {'-'}, {'-'}, {'ー'}, {'一'}}},
|
||||||
|
{[][]rune{{'9'}, {'9'}}},
|
||||||
|
{[][]rune{{'8'}, {'8'}}},
|
||||||
|
{[][]rune{{'7'}, {'7'}}},
|
||||||
|
{[][]rune{{'6'}, {'6'}}},
|
||||||
|
{[][]rune{{'5'}, {'5'}}},
|
||||||
|
{[][]rune{{'4'}, {'4'}}},
|
||||||
|
{[][]rune{{'3'}, {'3'}}},
|
||||||
|
{[][]rune{{'2'}, {'2'}}},
|
||||||
|
{[][]rune{{'1'}, {'1'}}},
|
||||||
|
{[][]rune{{'ぽ'}, {'ポ'}, {'ホ', '゚'}, {'ほ', '゚'}, {'ホ', '゚'}, {'ホ', '°'}, {'ほ', '°'}, {'ホ', '°'}}},
|
||||||
|
{[][]rune{{'ぺ'}, {'ペ'}, {'ヘ', '゚'}, {'へ', '゚'}, {'ヘ', '゚'}, {'ヘ', '°'}, {'へ', '°'}, {'ヘ', '°'}}},
|
||||||
|
{[][]rune{{'ぷ'}, {'プ'}, {'フ', '゚'}, {'ふ', '゚'}, {'フ', '゚'}, {'フ', '°'}, {'ふ', '°'}, {'フ', '°'}}},
|
||||||
|
{[][]rune{{'ぴ'}, {'ピ'}, {'ヒ', '゚'}, {'ひ', '゚'}, {'ヒ', '゚'}, {'ヒ', '°'}, {'ひ', '°'}, {'ヒ', '°'}}},
|
||||||
|
{[][]rune{{'ぱ'}, {'パ'}, {'ハ', '゚'}, {'は', '゚'}, {'ハ', '゚'}, {'ハ', '°'}, {'は', '°'}, {'ハ', '°'}, {'八', '゚'}, {'八', '゜'}}},
|
||||||
|
{[][]rune{{'z'}, {'z'}, {'Z'}, {'Z'}, {'Ζ'}}},
|
||||||
|
{[][]rune{{'y'}, {'y'}, {'Y'}, {'Y'}, {'Υ'}, {'У'}, {'у'}}},
|
||||||
|
{[][]rune{{'x'}, {'x'}, {'X'}, {'X'}, {'Χ'}, {'χ'}, {'Х'}, {'×'}, {'х'}}},
|
||||||
|
{[][]rune{{'w'}, {'w'}, {'W'}, {'W'}, {'ω'}, {'Ш'}, {'ш'}, {'щ'}}},
|
||||||
|
{[][]rune{{'v'}, {'v'}, {'V'}, {'V'}, {'ν'}, {'υ'}}},
|
||||||
|
{[][]rune{{'u'}, {'u'}, {'U'}, {'U'}, {'μ'}, {'∪'}}},
|
||||||
|
{[][]rune{{'t'}, {'t'}, {'T'}, {'T'}, {'Τ'}, {'τ'}, {'Т'}, {'т'}}},
|
||||||
|
{[][]rune{{'s'}, {'s'}, {'S'}, {'S'}, {'∫'}, {'$'}, {'$'}}},
|
||||||
|
{[][]rune{{'r'}, {'r'}, {'R'}, {'R'}, {'Я'}, {'я'}}},
|
||||||
|
{[][]rune{{'q'}, {'q'}, {'Q'}, {'Q'}}},
|
||||||
|
{[][]rune{{'p'}, {'p'}, {'P'}, {'P'}, {'Ρ'}, {'ρ'}, {'Р'}, {'р'}}},
|
||||||
|
{[][]rune{{'o'}, {'o'}, {'O'}, {'O'}, {'○'}, {'Ο'}, {'ο'}, {'О'}, {'о'}, {'◯'}, {'〇'}, {'0'}, {'0'}}},
|
||||||
|
{[][]rune{{'n'}, {'n'}, {'N'}, {'N'}, {'Ν'}, {'η'}, {'ン'}, {'ん'}, {'ン'}}},
|
||||||
|
{[][]rune{{'m'}, {'m'}, {'M'}, {'M'}, {'Μ'}, {'М'}, {'м'}}},
|
||||||
|
{[][]rune{{'l'}, {'l'}, {'L'}, {'L'}, {'|'}}},
|
||||||
|
{[][]rune{{'k'}, {'k'}, {'K'}, {'K'}, {'Κ'}, {'κ'}, {'К'}, {'к'}}},
|
||||||
|
{[][]rune{{'j'}, {'j'}, {'J'}, {'J'}}},
|
||||||
|
{[][]rune{{'i'}, {'i'}, {'I'}, {'I'}, {'Ι'}}},
|
||||||
|
{[][]rune{{'h'}, {'h'}, {'H'}, {'H'}, {'Η'}, {'Н'}, {'н'}}},
|
||||||
|
{[][]rune{{'f'}, {'f'}, {'F'}, {'F'}}},
|
||||||
|
{[][]rune{{'g'}, {'g'}, {'G'}, {'G'}}},
|
||||||
|
{[][]rune{{'e'}, {'e'}, {'E'}, {'E'}, {'Ε'}, {'ε'}, {'Е'}, {'Ё'}, {'е'}, {'ё'}, {'∈'}}},
|
||||||
|
{[][]rune{{'d'}, {'d'}, {'D'}, {'D'}}},
|
||||||
|
{[][]rune{{'c'}, {'c'}, {'C'}, {'С'}, {'с'}, {'C'}, {'℃'}}},
|
||||||
|
{[][]rune{{'b'}, {'B'}, {'b'}, {'B'}, {'β'}, {'Β'}, {'В'}, {'в'}, {'ъ'}, {'ь'}, {'♭'}}},
|
||||||
|
{[][]rune{{'\''}, {'’'}}},
|
||||||
|
{[][]rune{{'a'}, {'A'}, {'a'}, {'A'}, {'α'}, {'@'}, {'@'}, {'а'}, {'Å'}, {'А'}, {'Α'}}},
|
||||||
|
{[][]rune{{'"'}, {'”'}}},
|
||||||
|
{[][]rune{{'%'}, {'%'}}},
|
||||||
|
}
|
||||||
|
for _, smcGroup := range smcData {
|
||||||
|
for _, smcPair := range smcGroup.charGroup {
|
||||||
|
smc.WriteUint16(stringsupport.ToNGWord(string(smcPair[0]))[0])
|
||||||
|
if len(smcPair) > 1 {
|
||||||
|
smc.WriteUint16(stringsupport.ToNGWord(string(smcPair[1]))[0])
|
||||||
|
} else {
|
||||||
|
smc.WriteUint16(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
smc.WriteUint32(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
filters.WriteUint32(uint32(len(smc.Data())))
|
||||||
|
filters.WriteBytes(smc.Data())
|
||||||
|
|
||||||
|
filters.WriteNullTerminatedBytes([]byte("nam"))
|
||||||
|
nam := byteframe.NewByteFrame()
|
||||||
|
nam.SetLE()
|
||||||
|
for _, word := range namNGWords {
|
||||||
|
parts := stringsupport.ToNGWord(word)
|
||||||
|
nam.WriteUint32(uint32(len(parts)))
|
||||||
|
for _, part := range parts {
|
||||||
|
nam.WriteUint16(part)
|
||||||
|
var i int16
|
||||||
|
j := int16(-1)
|
||||||
|
for _, smcGroup := range smcData {
|
||||||
|
if rune(part) == rune(stringsupport.ToNGWord(string(smcGroup.charGroup[0][0]))[0]) {
|
||||||
|
j = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i += int16(len(smcGroup.charGroup) + 1)
|
||||||
|
}
|
||||||
|
nam.WriteInt16(j)
|
||||||
|
}
|
||||||
|
nam.WriteUint16(0)
|
||||||
|
nam.WriteInt16(-1)
|
||||||
|
}
|
||||||
|
filters.WriteUint32(uint32(len(nam.Data())))
|
||||||
|
filters.WriteBytes(nam.Data())
|
||||||
|
|
||||||
|
filters.WriteNullTerminatedBytes([]byte("msg"))
|
||||||
|
msg := byteframe.NewByteFrame()
|
||||||
|
msg.SetLE()
|
||||||
|
for _, word := range msgNGWords {
|
||||||
|
parts := stringsupport.ToNGWord(word)
|
||||||
|
msg.WriteUint32(uint32(len(parts)))
|
||||||
|
for _, part := range parts {
|
||||||
|
msg.WriteUint16(part)
|
||||||
|
var i int16
|
||||||
|
j := int16(-1)
|
||||||
|
for _, smcGroup := range smcData {
|
||||||
|
if rune(part) == rune(stringsupport.ToNGWord(string(smcGroup.charGroup[0][0]))[0]) {
|
||||||
|
j = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i += int16(len(smcGroup.charGroup) + 1)
|
||||||
|
}
|
||||||
|
msg.WriteInt16(j)
|
||||||
|
}
|
||||||
|
msg.WriteUint16(0)
|
||||||
|
msg.WriteInt16(-1)
|
||||||
|
}
|
||||||
|
filters.WriteUint32(uint32(len(msg.Data())))
|
||||||
|
filters.WriteBytes(msg.Data())
|
||||||
|
|
||||||
|
bf.WriteUint16(uint16(len(filters.Data())))
|
||||||
|
bf.WriteBytes(filters.Data())
|
||||||
|
|
||||||
|
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 len(bf.DataFromCurrent()) < 128 {
|
if s.client != PS4 {
|
||||||
s.sendCode(SIGN_EABORT)
|
if len(bf.DataFromCurrent()) < 128 {
|
||||||
return
|
s.sendCode(SIGN_EABORT)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = bf.ReadNullTerminatedBytes() // VITA = 0000000256, PS3 = 0000000255
|
||||||
|
_ = bf.ReadBytes(2) // VITA = 1, PS3 = !
|
||||||
|
_ = bf.ReadBytes(82)
|
||||||
}
|
}
|
||||||
_ = bf.ReadNullTerminatedBytes() // VITA = 0000000256, PS3 = 0000000255
|
|
||||||
_ = bf.ReadBytes(2) // VITA = 1, PS3 = !
|
|
||||||
_ = 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