mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 23:54:33 +01:00
36
.github/workflows/go.yml
vendored
36
.github/workflows/go.yml
vendored
@@ -1,10 +1,10 @@
|
||||
name: Erupe
|
||||
name: Build
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -14,14 +14,32 @@ jobs:
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- name: Build
|
||||
run: cd Erupe && go build -v
|
||||
- name: Build Linux-amd64
|
||||
run: env GOOS=linux GOARCH=amd64 go build -v
|
||||
|
||||
- name: Upload artifacts
|
||||
- name: Upload Linux-amd64 artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Erupe
|
||||
name: Linux-amd64
|
||||
path: |
|
||||
./Erupe/erupe-ce.exe
|
||||
./Erupe/config.json
|
||||
./Erupe/www/
|
||||
./erupe-ce
|
||||
./config.json
|
||||
./www/
|
||||
./savedata/
|
||||
./bin/
|
||||
./RoadShopItems.csv
|
||||
|
||||
- name: Build Windows-amd64
|
||||
run: env GOOS=windows GOARCH=amd64 go build -v
|
||||
|
||||
- name: Upload Windows-amd64 artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Windows-amd64
|
||||
path: |
|
||||
./erupe-ce.exe
|
||||
./config.json
|
||||
./www/
|
||||
./savedata/
|
||||
./bin/
|
||||
./RoadShopItems.csv
|
||||
|
||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -1 +1,14 @@
|
||||
.idea/
|
||||
|
||||
www/jp/
|
||||
vendor/
|
||||
bin/*.bin
|
||||
bin/*.bak
|
||||
bin/quests/*.bin
|
||||
bin/questlists/*.bin
|
||||
bin/scenarios/*.bin
|
||||
bin/debug/*.bin
|
||||
savedata/*/
|
||||
*.exe
|
||||
*.lnk
|
||||
*.bat
|
||||
14
Erupe/.gitignore
vendored
14
Erupe/.gitignore
vendored
@@ -1,14 +0,0 @@
|
||||
www/jp/
|
||||
|
||||
.idea/
|
||||
vendor/
|
||||
bin/*.bin
|
||||
bin/*.bak
|
||||
bin/quests/*.bin
|
||||
bin/questlists/*.bin
|
||||
bin/scenarios/*.bin
|
||||
bin/debug/*.bin
|
||||
savedata/
|
||||
Erupe.exe
|
||||
*.lnk
|
||||
*.bat
|
||||
@@ -1 +0,0 @@
|
||||
0 is no event, 1 is "Week 1 Timestamp (broken), 2 is "Week 2 Timestamp (broken), 3 is Diva Defense
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,83 +0,0 @@
|
||||
# Erupe
|
||||
## WARNING
|
||||
This project is in its infancy and has no reliable active developer, no documentation, and no support.
|
||||
|
||||
# General info
|
||||
Currently allows a JP MHF client (with GameGuard removed) to:
|
||||
* Login and register an account (registration is automatic if account doesn't exist)
|
||||
* Create a character
|
||||
* Get ingame to the main city
|
||||
* See other players walk around
|
||||
* Do quests
|
||||
* Use chat*
|
||||
|
||||
# Installation
|
||||
## Server
|
||||
1. Clone the repo with `git clone https://github.com/Andoryuuta/Erupe.git`
|
||||
2. Install PostgreSQL
|
||||
3. Launch psql shell, `CREATE DATABASE erupe;`.
|
||||
4. Setup database with golang-migrate:
|
||||
|
||||
Windows:
|
||||
```
|
||||
> go get -tags 'postgres' -u github.com/golang-migrate/migrate/v4/cmd/migrate/
|
||||
|
||||
> set POSTGRESQL_URL=postgres://postgres:password@localhost:5432/erupe?sslmode=disable
|
||||
|
||||
> cd erupe
|
||||
|
||||
> migrate -database %POSTGRESQL_URL% -path migrations up
|
||||
```
|
||||
|
||||
Linux:
|
||||
```
|
||||
> go get -tags 'postgres' -u github.com/golang-migrate/migrate/v4/cmd/migrate/
|
||||
|
||||
> export POSTGRESQL_URL=postgres://postgres:password@localhost:5432/erupe?sslmode=disable
|
||||
|
||||
> cd erupe
|
||||
|
||||
> migrate -database $POSTGRESQL_URL -path migrations up
|
||||
```
|
||||
|
||||
(Replacing `postgres:password` with your postgres username and password)
|
||||
|
||||
5. Edit the config.json
|
||||
|
||||
Namely:
|
||||
* Update the database username and password
|
||||
* Update the `host_ip` and `ip` fields (there are multiple) to your external IP if you are hosting for multiple clients.
|
||||
|
||||
6. Place quest/scenario binaries.
|
||||
|
||||
The quest and scenario binary files should be placed in `bin/quests/` and `bin/scenarios` respectively.
|
||||
|
||||
## Launcher
|
||||
Erupe ships with a rudimentary custom launcher, so you don't need to obtain the original TW/JP files to simply get ingame. However, it does still support using the original files if you choose to. To set this up, place a copy of the original launcher html/js/css in `./www/tw/`, and `/www/jp/` for the TW and JP files respectively.
|
||||
|
||||
Then, modify the the `/launcher/js/launcher.js` file as such:
|
||||
* Find the call to `startUpdateProcess();` in a case statement and replace it with `finishUpdateProcess();`. (This disables the file check and updating)
|
||||
* (JP ONLY): replace all uses of "https://" with "http://" in the file.
|
||||
|
||||
Finally, edit the config.json and set `UseOriginalLauncherFiles` to `true` under the launcher settings.
|
||||
|
||||
# Usage
|
||||
### Note: If you are switching to/from the custom launcher html, you will have to clear your IE cache @ `C:\Users\<user>\AppData\Local\Microsoft\Windows\INetCache`.
|
||||
|
||||
## Server
|
||||
```
|
||||
cd Erupe
|
||||
go run .
|
||||
```
|
||||
|
||||
## Client
|
||||
Add to hosts:
|
||||
```
|
||||
127.0.0.1 mhfg.capcom.com.tw
|
||||
127.0.0.1 mhf-n.capcom.com.tw
|
||||
127.0.0.1 cog-members.mhf-z.jp
|
||||
127.0.0.1 www.capcom-onlinegames.jp
|
||||
127.0.0.1 srv-mhf.capcom-networks.jp
|
||||
```
|
||||
|
||||
Run mhf.exe normally (with locale emulator or appropriate timezone).
|
||||
@@ -1,5 +0,0 @@
|
||||
BEGIN;
|
||||
|
||||
CREATE SEQUENCE IF NOT EXISTS public.airou_id_seq;
|
||||
|
||||
END;
|
||||
@@ -1,46 +0,0 @@
|
||||
package pascalstring
|
||||
|
||||
import (
|
||||
"erupe-ce/common/byteframe"
|
||||
"golang.org/x/text/encoding/japanese"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
func Uint8(bf *byteframe.ByteFrame, x string, t bool) {
|
||||
if t {
|
||||
e := japanese.ShiftJIS.NewEncoder()
|
||||
xt, _, err := transform.String(e, x)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
x = xt
|
||||
}
|
||||
bf.WriteUint8(uint8(len(x) + 1))
|
||||
bf.WriteNullTerminatedBytes([]byte(x))
|
||||
}
|
||||
|
||||
func Uint16(bf *byteframe.ByteFrame, x string, t bool) {
|
||||
if t {
|
||||
e := japanese.ShiftJIS.NewEncoder()
|
||||
xt, _, err := transform.String(e, x)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
x = xt
|
||||
}
|
||||
bf.WriteUint16(uint16(len(x) + 1))
|
||||
bf.WriteNullTerminatedBytes([]byte(x))
|
||||
}
|
||||
|
||||
func Uint32(bf *byteframe.ByteFrame, x string, t bool) {
|
||||
if t {
|
||||
e := japanese.ShiftJIS.NewEncoder()
|
||||
xt, _, err := transform.String(e, x)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
x = xt
|
||||
}
|
||||
bf.WriteUint32(uint32(len(x) + 1))
|
||||
bf.WriteNullTerminatedBytes([]byte(x))
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
BEGIN;
|
||||
CREATE TABLE IF NOT EXISTS public.distribution
|
||||
(
|
||||
id serial NOT NULL PRIMARY KEY,
|
||||
character_id int,
|
||||
type int NOT NULL,
|
||||
deadline timestamp without time zone,
|
||||
event_name text NOT NULL DEFAULT 'GM Gift!',
|
||||
description text NOT NULL DEFAULT '~C05You received a gift!',
|
||||
times_acceptable int NOT NULL DEFAULT 1,
|
||||
min_hr int NOT NULL DEFAULT 65535,
|
||||
max_hr int NOT NULL DEFAULT 65535,
|
||||
min_sr int NOT NULL DEFAULT 65535,
|
||||
max_sr int NOT NULL DEFAULT 65535,
|
||||
min_gr int NOT NULL DEFAULT 65535,
|
||||
max_gr int NOT NULL DEFAULT 65535,
|
||||
data bytea NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.distributions_accepted
|
||||
(
|
||||
distribution_id int,
|
||||
character_id int
|
||||
);
|
||||
|
||||
END;
|
||||
@@ -1,26 +0,0 @@
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE IF EXISTS public.gook
|
||||
DROP COLUMN IF EXISTS gook0status;
|
||||
|
||||
ALTER TABLE IF EXISTS public.gook
|
||||
DROP COLUMN IF EXISTS gook1status;
|
||||
|
||||
ALTER TABLE IF EXISTS public.gook
|
||||
DROP COLUMN IF EXISTS gook2status;
|
||||
|
||||
ALTER TABLE IF EXISTS public.gook
|
||||
DROP COLUMN IF EXISTS gook3status;
|
||||
|
||||
ALTER TABLE IF EXISTS public.gook
|
||||
DROP COLUMN IF EXISTS gook4status;
|
||||
|
||||
ALTER TABLE IF EXISTS public.gook
|
||||
DROP COLUMN IF EXISTS gook5status;
|
||||
|
||||
ALTER TABLE IF EXISTS public.gook
|
||||
DROP COLUMN IF EXISTS gook5;
|
||||
|
||||
UPDATE public.gook SET gook0=NULL, gook1=NULL, gook2=NULL, gook3=NULL, gook4=NULL;
|
||||
|
||||
END;
|
||||
@@ -1,56 +0,0 @@
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE IF EXISTS public.guilds
|
||||
ADD COLUMN IF NOT EXISTS pugi_name_1 varchar(12) DEFAULT '';
|
||||
ALTER TABLE IF EXISTS public.guilds
|
||||
ADD COLUMN IF NOT EXISTS pugi_name_2 varchar(12) DEFAULT '';
|
||||
ALTER TABLE IF EXISTS public.guilds
|
||||
ADD COLUMN IF NOT EXISTS pugi_name_3 varchar(12) DEFAULT '';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.guild_alliances
|
||||
(
|
||||
id serial NOT NULL PRIMARY KEY,
|
||||
name varchar(24) NOT NULL,
|
||||
created_at timestamp without time zone NOT NULL DEFAULT now(),
|
||||
parent_id int NOT NULL,
|
||||
sub1_id int,
|
||||
sub2_id int
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.guild_adventures
|
||||
(
|
||||
id serial NOT NULL PRIMARY KEY,
|
||||
guild_id int NOT NULL,
|
||||
destination int NOT NULL,
|
||||
charge int NOT NULL DEFAULT 0,
|
||||
depart int NOT NULL,
|
||||
return int NOT NULL,
|
||||
collected_by text NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.guild_meals
|
||||
(
|
||||
id serial NOT NULL PRIMARY KEY,
|
||||
guild_id int NOT NULL,
|
||||
meal_id int NOT NULL,
|
||||
level int NOT NULL,
|
||||
expires int NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.guild_hunts
|
||||
(
|
||||
id serial NOT NULL PRIMARY KEY,
|
||||
guild_id int NOT NULL,
|
||||
host_id int NOT NULL,
|
||||
destination int NOT NULL,
|
||||
level int NOT NULL,
|
||||
return int NOT NULL,
|
||||
acquired bool NOT NULL DEFAULT false,
|
||||
claimed bool NOT NULL DEFAULT false,
|
||||
hunters text NOT NULL DEFAULT '',
|
||||
treasure text NOT NULL DEFAULT '',
|
||||
hunt_data bytea NOT NULL,
|
||||
cats_used text NOT NULL
|
||||
);
|
||||
|
||||
END;
|
||||
@@ -1,6 +0,0 @@
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE IF EXISTS public.characters
|
||||
ADD COLUMN IF NOT EXISTS house bytea;
|
||||
|
||||
END;
|
||||
@@ -1,35 +0,0 @@
|
||||
BEGIN;
|
||||
CREATE TABLE IF NOT EXISTS public.normal_shop_items
|
||||
(
|
||||
shoptype integer,
|
||||
shopid integer,
|
||||
itemhash integer not null,
|
||||
itemid integer,
|
||||
points integer,
|
||||
tradequantity integer,
|
||||
rankreqlow integer,
|
||||
rankreqhigh integer,
|
||||
rankreqg integer,
|
||||
storelevelreq integer,
|
||||
maximumquantity integer,
|
||||
boughtquantity integer,
|
||||
roadfloorsrequired integer,
|
||||
weeklyfataliskills integer,
|
||||
enable_weeks character varying(8)
|
||||
);
|
||||
|
||||
ALTER TABLE IF EXISTS public.normal_shop_items
|
||||
ADD COLUMN IF NOT EXISTS enable_weeks character varying(8);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.shop_item_state
|
||||
(
|
||||
char_id bigint REFERENCES characters (id),
|
||||
itemhash int UNIQUE NOT NULL,
|
||||
usedquantity int,
|
||||
week int
|
||||
);
|
||||
|
||||
ALTER TABLE IF EXISTS public.shop_item_state
|
||||
ADD COLUMN IF NOT EXISTS week int;
|
||||
|
||||
END;
|
||||
@@ -1,9 +0,0 @@
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE IF EXISTS public.characters
|
||||
ADD COLUMN IF NOT EXISTS scenariodata bytea;
|
||||
|
||||
ALTER TABLE IF EXISTS public.characters
|
||||
ADD COLUMN IF NOT EXISTS savefavoritequest bytea;
|
||||
|
||||
END;
|
||||
@@ -1,122 +0,0 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
)
|
||||
|
||||
func handleMsgSysInsertUser(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgSysDeleteUser(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func broadcastNewUser(s *Session) {
|
||||
s.logger.Debug(fmt.Sprintf("Broadcasting new user: %s (%d)", s.Name, s.charID))
|
||||
|
||||
clientNotif := byteframe.NewByteFrame()
|
||||
var temp mhfpacket.MHFPacket
|
||||
for _, session := range s.server.sessions {
|
||||
if session == s || !session.binariesDone {
|
||||
continue
|
||||
}
|
||||
temp = &mhfpacket.MsgSysInsertUser{CharID: session.charID}
|
||||
clientNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(clientNotif, s.clientContext)
|
||||
for i := 0; i < 3; i++ {
|
||||
temp = &mhfpacket.MsgSysNotifyUserBinary{
|
||||
CharID: session.charID,
|
||||
BinaryType: uint8(i + 1),
|
||||
}
|
||||
clientNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(clientNotif, s.clientContext)
|
||||
}
|
||||
}
|
||||
s.QueueSend(clientNotif.Data())
|
||||
|
||||
serverNotif := byteframe.NewByteFrame()
|
||||
temp = &mhfpacket.MsgSysInsertUser{CharID: s.charID}
|
||||
serverNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(serverNotif, s.clientContext)
|
||||
for i := 0; i < 3; i++ {
|
||||
temp = &mhfpacket.MsgSysNotifyUserBinary{
|
||||
CharID: s.charID,
|
||||
BinaryType: uint8(i + 1),
|
||||
}
|
||||
serverNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(serverNotif, s.clientContext)
|
||||
}
|
||||
for _, session := range s.server.sessions {
|
||||
if session == s || !session.binariesDone {
|
||||
continue
|
||||
}
|
||||
session.QueueSend(serverNotif.Data())
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgSysSetUserBinary)
|
||||
s.server.userBinaryPartsLock.Lock()
|
||||
s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: pkt.BinaryType}] = pkt.RawDataPayload
|
||||
s.server.userBinaryPartsLock.Unlock()
|
||||
|
||||
// Insert user once all binary parts exist
|
||||
if !s.binariesDone {
|
||||
for i := 0; i < 3; i++ {
|
||||
_, exists := s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: uint8(i + 1)}]
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
}
|
||||
s.binariesDone = true
|
||||
broadcastNewUser(s)
|
||||
return
|
||||
}
|
||||
|
||||
msg := &mhfpacket.MsgSysNotifyUserBinary{
|
||||
CharID: s.charID,
|
||||
BinaryType: pkt.BinaryType,
|
||||
}
|
||||
|
||||
s.server.BroadcastMHF(msg, s)
|
||||
}
|
||||
|
||||
func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgSysGetUserBinary)
|
||||
|
||||
// Try to get the data.
|
||||
s.server.userBinaryPartsLock.RLock()
|
||||
defer s.server.userBinaryPartsLock.RUnlock()
|
||||
data, ok := s.server.userBinaryParts[userBinaryPartID{charID: pkt.CharID, index: pkt.BinaryType}]
|
||||
resp := byteframe.NewByteFrame()
|
||||
|
||||
// If we can't get the real data, use a placeholder.
|
||||
if !ok {
|
||||
if pkt.BinaryType == 1 {
|
||||
// Stub name response with character ID
|
||||
resp.WriteBytes([]byte(fmt.Sprintf("CID%d", s.charID)))
|
||||
resp.WriteUint8(0) // NULL terminator.
|
||||
} else if pkt.BinaryType == 2 {
|
||||
data, err := base64.StdEncoding.DecodeString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBn8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAwAAAAAAAAAAAAAABAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
resp.WriteBytes(data)
|
||||
} else if pkt.BinaryType == 3 {
|
||||
data, err := base64.StdEncoding.DecodeString("AQAAA2ea5P8ATgEA/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBn8AAAAAAAAAAAABAKAMAAAAAAAAAAAAACgAAAAAAAAAAAABAsQOAAAAAAAAAAABA6UMAAAAAAAAAAABBKAMAAAAAAAAAAABBToNAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAgACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
resp.WriteBytes(data)
|
||||
}
|
||||
} else {
|
||||
resp.WriteBytes(data)
|
||||
}
|
||||
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
|
||||
func handleMsgSysNotifyUserBinary(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetBbsUserStatus(s *Session, p mhfpacket.MHFPacket) {}
|
||||
@@ -1,32 +0,0 @@
|
||||
BEGIN;
|
||||
|
||||
DROP TABLE IF EXISTS public.sign_sessions;
|
||||
CREATE TABLE IF NOT EXISTS public.sign_sessions
|
||||
(
|
||||
user_id int NOT NULL,
|
||||
char_id int,
|
||||
token varchar(16) NOT NULL,
|
||||
server_id integer
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS public.servers;
|
||||
CREATE TABLE IF NOT EXISTS public.servers
|
||||
(
|
||||
server_id int NOT NULL,
|
||||
season int NOT NULL,
|
||||
current_players int NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE IF EXISTS public.characters
|
||||
ADD COLUMN IF NOT EXISTS deleted boolean NOT NULL DEFAULT false;
|
||||
|
||||
ALTER TABLE IF EXISTS public.characters
|
||||
ADD COLUMN IF NOT EXISTS friends text NOT NULL DEFAULT '';
|
||||
|
||||
ALTER TABLE IF EXISTS public.characters
|
||||
ADD COLUMN IF NOT EXISTS blocked text NOT NULL DEFAULT '';
|
||||
|
||||
ALTER TABLE IF EXISTS public.users
|
||||
ADD COLUMN IF NOT EXISTS last_character int DEFAULT 0;
|
||||
|
||||
END;
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 The Erupe Developers from Einherjar Team
|
||||
Copyright (c) 2019 The Erupe Developers, The Erupe Developers from Einherjar Team, ZeruLight
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,8 @@
|
||||
# Erupe Community Edition
|
||||
|
||||
This is a community upload of a community project. The amount of people who worked on it is innumerous, and hard to keep track of. Credits to Andoryuuta, Fist's Team, the French Team, Mai's Team and many others. No matter the relations, these files will remain public and open source, free for all to use and modify.
|
||||
|
||||
A pastebin with various links, tips, and FAQ: https://pastebin.com/QqAwZSTC
|
||||
[A pastebin with various links, tips, and FAQ](https://pastebin.com/QqAwZSTC)
|
||||
|
||||
An upload for the quest and scenario files exists here: https://github.com/xl3lackout/MHFZ-Quest-Files
|
||||
(Over 300k+ files)
|
||||
[An upload for the quest and scenario files exists here](https://github.com/xl3lackout/MHFZ-Quest-Files)
|
||||
(Over 300k+ files)
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
cd PUT ERUPE DIRECTORY HERE - BE SURE TO USE /d IF ITS ON A DRIVE OTHER THAN C
|
||||
go run .
|
||||
46
common/pascalstring/pascalstring.go
Normal file
46
common/pascalstring/pascalstring.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package pascalstring
|
||||
|
||||
import (
|
||||
"erupe-ce/common/byteframe"
|
||||
"golang.org/x/text/encoding/japanese"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
func Uint8(bf *byteframe.ByteFrame, x string, t bool) {
|
||||
if t {
|
||||
e := japanese.ShiftJIS.NewEncoder()
|
||||
xt, _, err := transform.String(e, x)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
x = xt
|
||||
}
|
||||
bf.WriteUint8(uint8(len(x) + 1))
|
||||
bf.WriteNullTerminatedBytes([]byte(x))
|
||||
}
|
||||
|
||||
func Uint16(bf *byteframe.ByteFrame, x string, t bool) {
|
||||
if t {
|
||||
e := japanese.ShiftJIS.NewEncoder()
|
||||
xt, _, err := transform.String(e, x)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
x = xt
|
||||
}
|
||||
bf.WriteUint16(uint16(len(x) + 1))
|
||||
bf.WriteNullTerminatedBytes([]byte(x))
|
||||
}
|
||||
|
||||
func Uint32(bf *byteframe.ByteFrame, x string, t bool) {
|
||||
if t {
|
||||
e := japanese.ShiftJIS.NewEncoder()
|
||||
xt, _, err := transform.String(e, x)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
x = xt
|
||||
}
|
||||
bf.WriteUint32(uint32(len(x) + 1))
|
||||
bf.WriteNullTerminatedBytes([]byte(x))
|
||||
}
|
||||
@@ -106,7 +106,7 @@ func PaddedString(x string, size uint, t bool) []byte {
|
||||
e := japanese.ShiftJIS.NewEncoder()
|
||||
xt, _, err := transform.String(e, x)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return make([]byte, size)
|
||||
}
|
||||
x = xt
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
{
|
||||
"host_ip": "127.0.0.1",
|
||||
"bin_path": "bin",
|
||||
"Host": "127.0.0.1",
|
||||
"BinPath": "bin",
|
||||
"DisableSoftCrash": false,
|
||||
"devmode": true,
|
||||
"devmodeoptions": {
|
||||
"serverName" : "",
|
||||
"hideLoginNotice": false,
|
||||
"loginNotice": "<BODY><CENTER><SIZE_3><C_4>Welcome to Erupe SU9 (Patch 1)!<BR><BODY><LEFT><SIZE_2><C_5>Erupe is experimental software<C_7>, we are not liable for any<BR><BODY>issues caused by installing the software!<BR><BODY><BR><BODY><C_4>■Report bugs on Discord!<C_7><BR><BODY><BR><BODY><C_4>■Test everything!<C_7><BR><BODY><BR><BODY><C_4>■Don't talk to softlocking NPCs!<C_7><BR><BODY><BR><BODY><C_4>■Fork the code on GitHub!<C_7><BR><BODY><BR><BODY>Thank you to all of the contributors,<BR><BODY><BR><BODY>this wouldn't exist without you.",
|
||||
"cleandb": false,
|
||||
"maxlauncherhr": true,
|
||||
"maxlauncherhr": false,
|
||||
"LogInboundMessages": false,
|
||||
"LogOutboundMessages": false,
|
||||
"MaxHexdumpLength": 256,
|
||||
@@ -14,6 +17,9 @@
|
||||
"FestaEvent": 0,
|
||||
"TournamentEvent": 0,
|
||||
"MezFesEvent": true,
|
||||
"MezFesAlt": false,
|
||||
"DisableMailItems": true,
|
||||
"DisableTokenCheck": false,
|
||||
"SaveDumps": {
|
||||
"Enabled": true,
|
||||
"OutputDir": "savedata"
|
||||
@@ -47,34 +53,34 @@
|
||||
{
|
||||
"name": "Newbie", "description": "", "ip": "", "type": 3, "recommended": 2, "allowedclientflags": 0,
|
||||
"channels": [
|
||||
{ "port": 54001, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 },
|
||||
{ "port": 54002, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
|
||||
{ "port": 54001, "MaxPlayers": 100 },
|
||||
{ "port": 54002, "MaxPlayers": 100 }
|
||||
]
|
||||
}, {
|
||||
"name": "Normal", "description": "", "ip": "", "type": 1, "recommended": 0, "allowedclientflags": 0,
|
||||
"channels": [
|
||||
{ "port": 54003, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 },
|
||||
{ "port": 54004, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
|
||||
{ "port": 54003, "MaxPlayers": 100 },
|
||||
{ "port": 54004, "MaxPlayers": 100 }
|
||||
]
|
||||
}, {
|
||||
"name": "Cities", "description": "", "ip": "", "type": 2, "recommended": 0, "allowedclientflags": 0,
|
||||
"channels": [
|
||||
{ "port": 54005, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
|
||||
{ "port": 54005, "MaxPlayers": 100 }
|
||||
]
|
||||
}, {
|
||||
"name": "Tavern", "description": "", "ip": "", "type": 4, "recommended": 0, "allowedclientflags": 0,
|
||||
"channels": [
|
||||
{ "port": 54006, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
|
||||
{ "port": 54006, "MaxPlayers": 100 }
|
||||
]
|
||||
}, {
|
||||
"name": "Return", "description": "", "ip": "", "type": 5, "recommended": 0, "allowedclientflags": 0,
|
||||
"channels": [
|
||||
{ "port": 54007, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
|
||||
{ "port": 54007, "MaxPlayers": 100 }
|
||||
]
|
||||
}, {
|
||||
"name": "MezFes", "description": "", "ip": "", "type": 6, "recommended": 6, "allowedclientflags": 0,
|
||||
"channels": [
|
||||
{ "port": 54008, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
|
||||
{ "port": 54008, "MaxPlayers": 100 }
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -9,9 +9,10 @@ import (
|
||||
|
||||
// Config holds the global server-wide config.
|
||||
type Config struct {
|
||||
HostIP string `mapstructure:"host_ip"`
|
||||
BinPath string `mapstructure:"bin_path"`
|
||||
DevMode bool
|
||||
Host string `mapstructure:"Host"`
|
||||
BinPath string `mapstructure:"BinPath"`
|
||||
DisableSoftCrash bool // Disables the 'Press Return to exit' dialog allowing scripts to reboot the server automatically
|
||||
DevMode bool
|
||||
|
||||
DevModeOptions DevModeOptions
|
||||
Discord Discord
|
||||
@@ -24,6 +25,8 @@ type Config struct {
|
||||
// DevModeOptions holds various debug/temporary options for use while developing Erupe.
|
||||
type DevModeOptions struct {
|
||||
ServerName string // To get specific instance server about (Current Players/Event Week)
|
||||
HideLoginNotice bool // Hide the Erupe notice on login
|
||||
LoginNotice string // MHFML string of the login notice displayed
|
||||
CleanDB bool // Automatically wipes the DB on server reset.
|
||||
MaxLauncherHR bool // Sets the HR returned in the launcher to HR9 so that you can join non-beginner worlds.
|
||||
FixedStageID bool // Causes all move_stage to use the ID sl1Ns200p0a0u0 to get you into all stages
|
||||
@@ -34,6 +37,9 @@ type DevModeOptions struct {
|
||||
FestaEvent int // Hunter's Festa event status
|
||||
TournamentEvent int // VS Tournament event status
|
||||
MezFesEvent bool // MezFes status
|
||||
MezFesAlt bool // Swaps out Volpakkun for Tokotoko
|
||||
DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!)
|
||||
DisableMailItems bool // Hack to prevent english versions of MHF from crashing
|
||||
SaveDumps SaveDumpOptions
|
||||
}
|
||||
|
||||
@@ -44,8 +50,8 @@ type SaveDumpOptions struct {
|
||||
|
||||
// Discord holds the discord integration config.
|
||||
type Discord struct {
|
||||
Enabled bool
|
||||
BotToken string
|
||||
Enabled bool
|
||||
BotToken string
|
||||
ServerID string
|
||||
RealtimeChannelID string
|
||||
DevRoles []string
|
||||
@@ -80,11 +86,11 @@ type Entrance struct {
|
||||
|
||||
// EntranceServerInfo represents an entry in the serverlist.
|
||||
type EntranceServerInfo struct {
|
||||
IP string
|
||||
Type uint8 // Server type. 0=?, 1=open, 2=cities, 3=newbie, 4=bar
|
||||
Season uint8 // Server activity. 0 = green, 1 = orange, 2 = blue
|
||||
IP string
|
||||
Type uint8 // Server type. 0=?, 1=open, 2=cities, 3=newbie, 4=bar
|
||||
Season uint8 // Server activity. 0 = green, 1 = orange, 2 = blue
|
||||
Recommended uint8 // Something to do with server recommendation on 0, 3, and 5.
|
||||
Name string // Server name, 66 byte null terminated Shift-JIS(JP) or Big5(TW).
|
||||
Name string // Server name, 66 byte null terminated Shift-JIS(JP) or Big5(TW).
|
||||
Description string // Server description
|
||||
// 4096(PC, PS3/PS4)?, 8258(PC, PS3/PS4)?, 8192 == nothing?
|
||||
// THIS ONLY EXISTS IF Binary8Header.type == "SV2", NOT "SVR"!
|
||||
@@ -98,9 +104,6 @@ type EntranceChannelInfo struct {
|
||||
Port uint16
|
||||
MaxPlayers uint16
|
||||
CurrentPlayers uint16
|
||||
Unk0 uint16
|
||||
Unk1 uint16
|
||||
Unk2 uint16
|
||||
}
|
||||
|
||||
// getOutboundIP4 gets the preferred outbound ip4 of this machine
|
||||
@@ -138,8 +141,8 @@ func LoadConfig() (*Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.HostIP == "" {
|
||||
c.HostIP = getOutboundIP4().To4().String()
|
||||
if c.Host == "" {
|
||||
c.Host = getOutboundIP4().To4().String()
|
||||
}
|
||||
|
||||
return c, nil
|
||||
BIN
erupe-bak.sql
BIN
erupe-bak.sql
Binary file not shown.
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
@@ -19,6 +19,8 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var erupeConfig *config.Config
|
||||
|
||||
// Temporary DB auto clean on startup for quick development & testing.
|
||||
func cleanDB(db *sqlx.DB) {
|
||||
_ = db.MustExec("DELETE FROM guild_characters")
|
||||
@@ -29,6 +31,7 @@ func cleanDB(db *sqlx.DB) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
zapLogger, _ := zap.NewDevelopment()
|
||||
defer zapLogger.Sync()
|
||||
logger := zapLogger.Named("main")
|
||||
@@ -36,7 +39,7 @@ func main() {
|
||||
logger.Info("Starting Erupe")
|
||||
|
||||
// Load the configuration.
|
||||
erupeConfig, err := config.LoadConfig()
|
||||
erupeConfig, err = config.LoadConfig()
|
||||
if err != nil {
|
||||
preventClose(fmt.Sprintf("Failed to load config: %s", err.Error()))
|
||||
}
|
||||
@@ -45,6 +48,19 @@ func main() {
|
||||
preventClose("Database password is blank")
|
||||
}
|
||||
|
||||
if net.ParseIP(erupeConfig.Host) == nil {
|
||||
ips, _ := net.LookupIP(erupeConfig.Host)
|
||||
for _, ip := range ips {
|
||||
if ip != nil {
|
||||
erupeConfig.Host = ip.String()
|
||||
break
|
||||
}
|
||||
}
|
||||
if net.ParseIP(erupeConfig.Host) == nil {
|
||||
preventClose("Invalid host address")
|
||||
}
|
||||
}
|
||||
|
||||
// Discord bot
|
||||
var discordBot *discordbot.DiscordBot = nil
|
||||
|
||||
@@ -151,9 +167,6 @@ func main() {
|
||||
ci := 0
|
||||
count := 1
|
||||
for _, ee := range erupeConfig.Entrance.Entries {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
// Randomly generate a season for the World
|
||||
season := rand.Intn(3) + 1
|
||||
for _, ce := range ee.Channels {
|
||||
sid := (4096 + si*256) + (16 + ci)
|
||||
c := *channelserver.NewServer(&channelserver.Config{
|
||||
@@ -163,11 +176,17 @@ func main() {
|
||||
DB: db,
|
||||
DiscordBot: discordBot,
|
||||
})
|
||||
err = c.Start(int(ce.Port))
|
||||
if ee.IP == "" {
|
||||
c.IP = erupeConfig.Host
|
||||
} else {
|
||||
c.IP = ee.IP
|
||||
}
|
||||
c.Port = ce.Port
|
||||
err = c.Start()
|
||||
if err != nil {
|
||||
preventClose(fmt.Sprintf("Failed to start channel server: %s", err.Error()))
|
||||
} else {
|
||||
channelQuery += fmt.Sprintf("INSERT INTO servers (server_id, season, current_players) VALUES (%d, %d, 0);", sid, season)
|
||||
channelQuery += fmt.Sprintf("INSERT INTO servers (server_id, season, current_players) VALUES (%d, %d, 0);", sid, si%3)
|
||||
channels = append(channels, &c)
|
||||
logger.Info(fmt.Sprintf("Started channel server %d on port %d", count, ce.Port))
|
||||
ci++
|
||||
@@ -209,6 +228,9 @@ func wait() {
|
||||
}
|
||||
|
||||
func preventClose(text string) {
|
||||
if erupeConfig.DisableSoftCrash {
|
||||
os.Exit(0)
|
||||
}
|
||||
fmt.Println("\nFailed to start Erupe:\n" + text)
|
||||
go wait()
|
||||
fmt.Println("\nPress Enter/Return to exit...")
|
||||
@@ -1,8 +0,0 @@
|
||||
To bring up fresh database:
|
||||
migrate.exe -database postgres://user:password@host:port/dbname?sslmode=disable -path /pathto/migrations up
|
||||
To tear down database
|
||||
migrate.exe -database postgres://user:password@host:port/dbname?sslmode=disable -path /pathto/migrations down
|
||||
|
||||
|
||||
More info:
|
||||
https://github.com/golang-migrate/migrate/releases/tag/v4.15.2
|
||||
Binary file not shown.
@@ -1,9 +1,9 @@
|
||||
package binpacket
|
||||
|
||||
import (
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/common/stringsupport"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/common/byteframe"
|
||||
)
|
||||
|
||||
type MsgBinMailNotify struct {
|
||||
@@ -1,8 +1,8 @@
|
||||
package binpacket
|
||||
|
||||
import (
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network"
|
||||
)
|
||||
|
||||
// MsgBinTargeted is a format used for some broadcast types
|
||||
@@ -87,4 +87,4 @@ func (c *CryptPacketHeader) Encode() ([]byte, error) {
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user