mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-12 23:14:36 +01:00
partially implement tokened sessions
This commit is contained in:
@@ -87,6 +87,10 @@ func main() {
|
||||
}
|
||||
logger.Info("Connected to database")
|
||||
|
||||
// Clear existing tokens
|
||||
_ = db.MustExec("DELETE FROM sign_sessions")
|
||||
_ = db.MustExec("DELETE FROM servers")
|
||||
|
||||
// Clean the DB if the option is on.
|
||||
if erupeConfig.DevMode && erupeConfig.DevModeOptions.CleanDB {
|
||||
logger.Info("Cleaning DB")
|
||||
@@ -137,26 +141,37 @@ func main() {
|
||||
logger.Info("Started sign server")
|
||||
|
||||
var channels []channelserver.Server
|
||||
channelQuery := ""
|
||||
si := 0
|
||||
ci := 0
|
||||
count := 1
|
||||
for _, ee := range erupeConfig.Entrance.Entries {
|
||||
for _, ce := range ee.Channels {
|
||||
sid := (4096 + si * 256) + (16 + ci)
|
||||
c := *channelserver.NewServer(&channelserver.Config{
|
||||
Logger: logger.Named("channel-"+fmt.Sprint(count)),
|
||||
ErupeConfig: erupeConfig,
|
||||
DB: db,
|
||||
DiscordBot: discordBot,
|
||||
ID: uint16(sid),
|
||||
Logger: logger.Named("channel-"+fmt.Sprint(count)),
|
||||
ErupeConfig: erupeConfig,
|
||||
DB: db,
|
||||
DiscordBot: discordBot,
|
||||
})
|
||||
err = c.Start(int(ce.Port))
|
||||
if err != nil {
|
||||
logger.Fatal("Failed to start channel", zap.Error(err))
|
||||
} else {
|
||||
channelQuery += fmt.Sprintf("INSERT INTO servers (server_id, season, current_players) VALUES (%d, 0, 0);", sid)
|
||||
channels = append(channels, c)
|
||||
logger.Info(fmt.Sprintf("Started channel server %d on port %d", count, ce.Port))
|
||||
ci++
|
||||
count++
|
||||
}
|
||||
channels = append(channels, c)
|
||||
count++
|
||||
}
|
||||
ci = 0
|
||||
si++
|
||||
}
|
||||
|
||||
_ = db.MustExec(channelQuery)
|
||||
|
||||
// Wait for exit or interrupt with ctrl+C.
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"erupe-ce/network/clientctx"
|
||||
"erupe-ce/network"
|
||||
@@ -36,8 +36,7 @@ func (m *MsgSysLogin) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContex
|
||||
m.CharID1 = bf.ReadUint32()
|
||||
m.HardcodedZero1 = bf.ReadUint16()
|
||||
m.LoginTokenStringLength = bf.ReadUint16()
|
||||
m.LoginTokenString = string(bf.ReadBytes(17)) // TODO(Andoryuuta): What encoding is this string?
|
||||
|
||||
m.LoginTokenString = string(bf.ReadNullTerminatedBytes())
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -155,15 +155,19 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
|
||||
s.Name = name
|
||||
s.charID = pkt.CharID0
|
||||
s.rights = rights
|
||||
s.token = pkt.LoginTokenString
|
||||
s.Unlock()
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // Unix timestamp
|
||||
|
||||
if s.server.erupeConfig.DevModeOptions.ServerName != "" {
|
||||
_, err := s.server.db.Exec("UPDATE servers SET current_players=$1 WHERE server_name=$2", uint32(len(s.server.sessions)), s.server.erupeConfig.DevModeOptions.ServerName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = s.server.db.Exec("UPDATE servers SET current_players=$1 WHERE server_id=$2", len(s.server.sessions), s.server.ID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = s.server.db.Exec("UPDATE sign_sessions SET server_id=$1 WHERE token=$2", s.server.ID, s.token)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = s.server.db.Exec("UPDATE characters SET last_login=$1 WHERE id=$2", Time_Current().Unix(), s.charID)
|
||||
@@ -194,11 +198,14 @@ func logoutPlayer(s *Session) {
|
||||
delete(s.server.sessions, s.rawConn)
|
||||
s.rawConn.Close()
|
||||
|
||||
if s.server.erupeConfig.DevModeOptions.ServerName != "" {
|
||||
_, err := s.server.db.Exec("UPDATE servers SET current_players=$1 WHERE server_name=$2", uint32(len(s.server.sessions)), s.server.erupeConfig.DevModeOptions.ServerName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err := s.server.db.Exec("UPDATE sign_sessions SET server_id=NULL WHERE token=$1", s.token)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = s.server.db.Exec("UPDATE servers SET current_players=$1 WHERE server_id=$2", len(s.server.sessions), s.server.ID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
s.server.Lock()
|
||||
@@ -213,7 +220,7 @@ func logoutPlayer(s *Session) {
|
||||
removeSessionFromStage(s)
|
||||
|
||||
var timePlayed int
|
||||
err := s.server.db.QueryRow("SELECT time_played FROM characters WHERE id = $1", s.charID).Scan(&timePlayed)
|
||||
_ = s.server.db.QueryRow("SELECT time_played FROM characters WHERE id = $1", s.charID).Scan(&timePlayed)
|
||||
|
||||
timePlayed = (int(Time_Current_Adjusted().Unix()) - int(s.sessionStart)) + timePlayed
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ const (
|
||||
|
||||
// Config struct allows configuring the server.
|
||||
type Config struct {
|
||||
ID uint16
|
||||
Logger *zap.Logger
|
||||
DB *sqlx.DB
|
||||
DiscordBot *discordbot.DiscordBot
|
||||
@@ -50,6 +51,7 @@ type userBinaryPartID struct {
|
||||
// Server is a MHF channel server.
|
||||
type Server struct {
|
||||
sync.Mutex
|
||||
ID uint16
|
||||
logger *zap.Logger
|
||||
db *sqlx.DB
|
||||
erupeConfig *config.Config
|
||||
@@ -137,6 +139,7 @@ func NewRaviente() *Raviente {
|
||||
// NewServer creates a new Server type.
|
||||
func NewServer(config *Config) *Server {
|
||||
s := &Server {
|
||||
ID: config.ID,
|
||||
logger: config.Logger,
|
||||
db: config.DB,
|
||||
erupeConfig: config.ErupeConfig,
|
||||
|
||||
@@ -34,6 +34,7 @@ type Session struct {
|
||||
logKey []byte
|
||||
sessionStart int64
|
||||
rights uint32
|
||||
token string
|
||||
|
||||
semaphore *Semaphore // Required for the stateful MsgSysUnreserveStage packet.
|
||||
|
||||
|
||||
@@ -11,17 +11,7 @@ import (
|
||||
"erupe-ce/server/channelserver"
|
||||
)
|
||||
|
||||
func paddedString(x string, size uint) []byte {
|
||||
out := make([]byte, size)
|
||||
copy(out, x)
|
||||
|
||||
// Null terminate it.
|
||||
out[len(out)-1] = 0
|
||||
return out
|
||||
}
|
||||
|
||||
// Server Entries
|
||||
var name string
|
||||
var season uint8
|
||||
|
||||
// Server Channels
|
||||
@@ -31,17 +21,10 @@ func encodeServerInfo(serverInfos []config.EntranceServerInfo, s *Server) []byte
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
for serverIdx, si := range serverInfos {
|
||||
err := s.db.QueryRow("SELECT server_name FROM servers WHERE server_name=$1", si.Name).Scan(&name)
|
||||
sid := (4096 + serverIdx * 256) + 16
|
||||
err := s.db.QueryRow("SELECT season FROM servers WHERE server_id=$1", sid).Scan(&season)
|
||||
if err != nil {
|
||||
_, err := s.db.Exec("INSERT INTO servers (server_name, season, current_players, event_id, event_expiration) VALUES ($1, $2, 0, 0, 0)", si.Name, si.Season)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
err := s.db.QueryRow("SELECT season FROM servers WHERE server_name=$1", si.Name).Scan(&season)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4()))
|
||||
bf.WriteUint16(16 + uint16(serverIdx))
|
||||
@@ -50,25 +33,18 @@ func encodeServerInfo(serverInfos []config.EntranceServerInfo, s *Server) []byte
|
||||
bf.WriteUint8(si.Type)
|
||||
bf.WriteUint8(season)
|
||||
bf.WriteUint8(si.Recommended)
|
||||
sjisName, err := stringsupport.ConvertUTF8ToShiftJIS(si.Name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sjisDesc, err := stringsupport.ConvertUTF8ToShiftJIS(si.Description)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
combined := append([]byte{0x00}, sjisName...)
|
||||
combined := append([]byte{0x00}, stringsupport.UTF8ToSJIS(si.Name)...)
|
||||
combined = append(combined, []byte{0x00}...)
|
||||
combined = append(combined, sjisDesc...)
|
||||
bf.WriteBytes(paddedString(string(combined), 66))
|
||||
combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...)
|
||||
bf.WriteBytes(stringsupport.PaddedString(string(combined), 66, false))
|
||||
bf.WriteUint32(si.AllowedClientFlags)
|
||||
|
||||
for channelIdx, ci := range si.Channels {
|
||||
sid = (4096 + serverIdx * 256) + (16 + channelIdx)
|
||||
bf.WriteUint16(ci.Port)
|
||||
bf.WriteUint16(16 + uint16(channelIdx))
|
||||
bf.WriteUint16(ci.MaxPlayers)
|
||||
err := s.db.QueryRow("SELECT current_players FROM servers WHERE server_name=$1", si.Name).Scan(¤tplayers)
|
||||
err := s.db.QueryRow("SELECT current_players FROM servers WHERE server_id=$1", sid).Scan(¤tplayers)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -87,9 +87,43 @@ type character struct {
|
||||
|
||||
func (s *Server) getCharactersForUser(uid int) ([]character, error) {
|
||||
characters := []character{}
|
||||
err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE user_id = $1", uid)
|
||||
err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE user_id = $1 AND deleted = false", uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return characters, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) deleteCharacter(cid int, token string) error {
|
||||
var verify int
|
||||
err := s.db.QueryRow("SELECT count(*) FROM sign_sessions WHERE token = $1", token).Scan(&verify)
|
||||
if err != nil {
|
||||
return err // Invalid token
|
||||
}
|
||||
_, err = s.db.Exec("UPDATE characters SET deleted = true WHERE id = $1", cid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unused
|
||||
func (s *Server) checkToken(uid int) (bool, error) {
|
||||
var exists int
|
||||
err := s.db.QueryRow("SELECT count(*) FROM sign_sessions WHERE user_id = $1", uid).Scan(&exists)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if exists > 0 {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (s *Server) registerToken(uid int, token string) error {
|
||||
_, err := s.db.Exec("INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2)", uid, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func (s *Session) makeSignInResp(uid int) []byte {
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
token := randSeq(16)
|
||||
// TODO: register token to db, users table
|
||||
s.server.registerToken(uid, token)
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
@@ -45,7 +45,7 @@ func (s *Session) makeSignInResp(uid int) []byte {
|
||||
bf.WriteUint8(1) // entrance server count
|
||||
bf.WriteUint8(uint8(len(chars))) // character count
|
||||
bf.WriteUint32(0xFFFFFFFF) // login_token_number
|
||||
bf.WriteBytes(stringsupport.PaddedString(token, 16, false)) // login_token (16 byte padded string)
|
||||
bf.WriteBytes([]byte(token)) // login_token
|
||||
bf.WriteUint32(uint32(time.Now().Unix())) // unk timestamp
|
||||
ps.Uint8(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.HostIP, s.server.erupeConfig.Entrance.Port), false)
|
||||
|
||||
|
||||
@@ -60,11 +60,9 @@ func (s *Session) handlePacket(pkt []byte) error {
|
||||
}
|
||||
case "DELETE:100":
|
||||
loginTokenString := string(bf.ReadNullTerminatedBytes())
|
||||
_ = loginTokenString
|
||||
characterID := bf.ReadUint32()
|
||||
|
||||
sugar.Infof("Got delete request for character ID: %v\n", characterID)
|
||||
sugar.Infof("remaining unknown data:\n%s\n", hex.Dump(bf.DataFromCurrent()))
|
||||
characterID := int(bf.ReadUint32())
|
||||
s.server.deleteCharacter(characterID, loginTokenString)
|
||||
sugar.Infof("Deleted character ID: %v\n", characterID)
|
||||
default:
|
||||
sugar.Infof("Got unknown request type %s, data:\n%s\n", reqType, hex.Dump(bf.DataFromCurrent()))
|
||||
}
|
||||
@@ -92,7 +90,6 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error {
|
||||
newCharaReq = true
|
||||
}
|
||||
|
||||
// TODO(Andoryuuta): remove plaintext password storage if this ever becomes more than a toy project.
|
||||
var (
|
||||
id int
|
||||
password string
|
||||
@@ -138,6 +135,13 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
// TODO: Need to auto delete user tokens after inactivity
|
||||
// exists, err := s.server.checkToken(id)
|
||||
// if err != nil {
|
||||
// s.logger.Info("Error checking for live tokens", zap.Error(err))
|
||||
// serverRespBytes = makeSignInFailureResp(SIGN_EABORT)
|
||||
// break
|
||||
// }
|
||||
serverRespBytes = s.makeSignInResp(id)
|
||||
} else {
|
||||
s.logger.Info("Passwords don't match!")
|
||||
|
||||
22
Erupe/tokensessions.sql
Normal file
22
Erupe/tokensessions.sql
Normal file
@@ -0,0 +1,22 @@
|
||||
BEGIN;
|
||||
|
||||
DROP TABLE IF EXISTS public.sign_sessions;
|
||||
CREATE TABLE IF NOT EXISTS public.sign_sessions
|
||||
(
|
||||
user_id int NOT NULL,
|
||||
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 deleted boolean NOT NULL DEFAULT false;
|
||||
|
||||
END;
|
||||
Reference in New Issue
Block a user