Skip to content

Commit

Permalink
Add cmangos database schema support.
Browse files Browse the repository at this point in the history
  • Loading branch information
walkline committed Feb 18, 2024
1 parent 7561686 commit fd906b5
Show file tree
Hide file tree
Showing 43 changed files with 586 additions and 115 deletions.
2 changes: 1 addition & 1 deletion apps/authserver/cmd/authserver/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func main() {

stmtsBuilder := repo.StatementsBuilderForSchema(sharedRepo.ParseSchemaType(conf.DBSchemaType))

authRepo, err := repo.NewAccountMySQLRepo(authDB, stmtsBuilder)
authRepo, err := repo.NewAccountMySQLRepo(authDB, stmtsBuilder, sharedRepo.ParseSchemaType(conf.DBSchemaType))
if err != nil {
log.Fatal().Err(err).Msg("can't create auth repo")
}
Expand Down
2 changes: 1 addition & 1 deletion apps/authserver/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type Config struct {
// Port is port that would be used to listen the game client
Port string `yaml:"port" env:"PORT" env-default:"3724"`

// DBSchemaType is the schema type of database. Supported values: "tc", "ac".
// DBSchemaType is the schema type of database. Supported values: "tc", "ac", "cm".
DBSchemaType string `yaml:"dbSchemaType" env:"DB_SCHEMA_TYPE" env-default:"tc"`

// AuthDBConnection is connection string to the auth database
Expand Down
33 changes: 31 additions & 2 deletions apps/authserver/repo/account_mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@ package repo
import (
"context"
"database/sql"

shrepo "github.com/walkline/ToCloud9/shared/repo"
"github.com/walkline/ToCloud9/shared/slices"
)

type accountMySQLRepo struct {
db *sql.DB

accountByUserStmt *sql.Stmt
updateAccountStmt *sql.Stmt

schemaType shrepo.SupportedSchemaType
}

func NewAccountMySQLRepo(db *sql.DB, stmtBuilder StatementsBuilder) (AccountRepo, error) {
func NewAccountMySQLRepo(db *sql.DB, stmtBuilder StatementsBuilder, schemaType shrepo.SupportedSchemaType) (AccountRepo, error) {
accountByUserStmt, err := db.Prepare(stmtBuilder.StmtForType(AuthStmtTypeGetAccountByUsername))
if err != nil {
return nil, err
Expand All @@ -27,6 +32,7 @@ func NewAccountMySQLRepo(db *sql.DB, stmtBuilder StatementsBuilder) (AccountRepo
db: db,
accountByUserStmt: accountByUserStmt,
updateAccountStmt: updateAccountStmt,
schemaType: schemaType,
}, nil
}

Expand All @@ -40,10 +46,33 @@ func (r *accountMySQLRepo) AccountByUserName(ctx context.Context, username strin
}
return nil, err
}

if r.schemaType == shrepo.SupportedSchemaTypeCMaNGOS {
slices.ReverseBytes(account.Verifier)
slices.ReverseBytes(account.Salt)
slices.ReverseBytes(account.SessionKeyAuth)
}

return account, nil
}

func (r *accountMySQLRepo) UpdateAccount(ctx context.Context, a *Account) error {
_, err := r.updateAccountStmt.ExecContext(ctx, a.Username, a.Salt, a.Verifier, a.SessionKeyAuth, a.Locked, a.LastIP, a.ID)
var salt, verifier, sessionKey []byte
if r.schemaType == shrepo.SupportedSchemaTypeCMaNGOS {
salt = make([]byte, len(a.Salt))
verifier = make([]byte, len(a.Verifier))
sessionKey = make([]byte, len(a.SessionKeyAuth))
copy(salt, a.Salt)
copy(verifier, a.Verifier)
copy(sessionKey, a.SessionKeyAuth)
slices.ReverseBytes(salt)
slices.ReverseBytes(verifier)
slices.ReverseBytes(sessionKey)
} else {
salt = a.Salt
verifier = a.Verifier
sessionKey = a.SessionKeyAuth
}
_, err := r.updateAccountStmt.ExecContext(ctx, a.Username, salt, verifier, sessionKey, a.Locked, a.LastIP, a.ID)
return err
}
2 changes: 2 additions & 0 deletions apps/authserver/repo/auth_stmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ func StatementsBuilderForSchema(schemaType repo.SupportedSchemaType) StatementsB
return trinityCoreStatementsBuilder{}
case repo.SupportedSchemaTypeAzerothCore:
return azerothCoreStatementsBuilder{}
case repo.SupportedSchemaTypeCMaNGOS:
return cmangosStatementsBuilder{}
}
panic("unk schema type")
}
22 changes: 22 additions & 0 deletions apps/authserver/repo/auth_stmt_cm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package repo

import (
"fmt"
)

type cmangosStatementsBuilder struct{}

func (b cmangosStatementsBuilder) StmtForType(t AuthStmtType) string {
switch t {
case AuthStmtTypeGetAccountByUsername:
return "SELECT id, username, UNHEX(s), UNHEX(v), UNHEX(sessionkey), locked, IFNULL(last_ip, '') FROM account WHERE username = ?"
case AuthStmtTypeUpdateAccountByID:
return "UPDATE account SET username = ?, s = HEX(?), v = HEX(?), sessionkey = HEX(?), locked = ?, last_ip = ? WHERE id = ?"
case AuthStmtTypeGetRealmList:
return "SELECT id, name, icon, realmflags, timezone, allowedSecurityLevel, CAST(realmbuilds AS SIGNED) FROM realmlist"
case AuthStmtTypeGetCharactersCountOnRealmsByAccount:
return "SELECT realmid, numchars FROM realmcharacters WHERE acctid = ?"
}

panic(fmt.Sprintf("unk stmt type %d", uint8(t)))
}
2 changes: 1 addition & 1 deletion apps/charserver/cmd/charserver/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func main() {

charDB := shrepo.NewCharactersDB()
charDB.SetDBForRealm(1, cdb)
charRepo := repo.NewCharactersMYSQL(charDB)
charRepo := repo.NewCharactersMYSQL(charDB, shrepo.ParseSchemaType(conf.DBSchemaType))

onlineCharsRepo := repo.NewCharactersOnlineInMem()
lbEventsConsumer := events.NewLoadBalancerConsumer(
Expand Down
3 changes: 3 additions & 0 deletions apps/charserver/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ type Config struct {
// Port is port that would be used to serve grpc server
Port string `yaml:"port" env:"PORT" env-default:"8991"`

// DBSchemaType is the schema type of database. Supported values: "tc", "ac", "cm".
DBSchemaType string `yaml:"dbSchemaType" env:"DB_SCHEMA_TYPE" env-default:"tc"`

// CharDBConnection is connection string to the characters database
CharDBConnection string `yaml:"charactersDB" env:"CHAR_DB_CONNECTION" env-default:"trinity:trinity@tcp(127.0.0.1:3306)/characters"`

Expand Down
49 changes: 7 additions & 42 deletions apps/charserver/repo/characters_mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,22 @@ package repo

import (
"context"
"fmt"
"strconv"
"strings"
"unicode"

shrepo "github.com/walkline/ToCloud9/shared/repo"
)

type CharactersPreparedStatements uint32

func (s CharactersPreparedStatements) Stmt() string {
switch s {
case StmtListCharactersToLogin:
return `SELECT c.guid, c.account, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z,
IFNULL(gm.guildid, 0), c.playerFlags, c.at_login, IFNULL(cp.entry, 0), IFNULL(cp.modelid, 0), IFNULL(cp.level, 0), c.equipmentCache, IFNULL(cb.guid, 0)
FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid
LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY c.guid`
case StmtCharacterToLogin:
return `SELECT c.guid, c.account, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z,
IFNULL(gm.guildid, 0), c.playerFlags, c.at_login, IFNULL(cp.entry, 0), IFNULL(cp.modelid, 0), IFNULL(cp.level, 0), c.equipmentCache, IFNULL(cb.guid, 0)
FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid
LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.guid = ? AND c.deleteInfos_Name IS NULL`
case StmtSelectAccountData:
return "SELECT type, time, data FROM account_data WHERE accountId = ?"
case StmtSelectCharacterWithName:
return "SELECT c.guid, account, race, class, gender, level, zone, map, position_x, position_y, position_z, IFNULL(gm.guildid, 0) FROM characters AS c LEFT JOIN guild_member AS gm ON c.guid = gm.guid WHERE name = ?"
}

panic(fmt.Errorf("unk stmt %d", s))
}

func (s CharactersPreparedStatements) ID() uint32 {
return uint32(s)
}

const (
StmtListCharactersToLogin CharactersPreparedStatements = iota
StmtCharacterToLogin
StmtSelectAccountData
StmtSelectCharacterWithName
)

type CharactersMYSQL struct {
db shrepo.CharactersDB
}

func NewCharactersMYSQL(db shrepo.CharactersDB) Characters {
db.SetPreparedStatement(StmtListCharactersToLogin)
db.SetPreparedStatement(StmtSelectAccountData)
db.SetPreparedStatement(StmtCharacterToLogin)
db.SetPreparedStatement(StmtSelectCharacterWithName)
func NewCharactersMYSQL(db shrepo.CharactersDB, schemaType shrepo.SupportedSchemaType) Characters {
db.SetPreparedStatement(StmtListCharactersToLogin.SchemeStatement(schemaType))
db.SetPreparedStatement(StmtSelectAccountData.SchemeStatement(schemaType))
db.SetPreparedStatement(StmtCharacterToLogin.SchemeStatement(schemaType))
db.SetPreparedStatement(StmtSelectCharacterWithName.SchemeStatement(schemaType))

return &CharactersMYSQL{
db: db,
Expand Down Expand Up @@ -91,7 +56,7 @@ func (c CharactersMYSQL) ListCharactersToLogIn(ctx context.Context, realmID, acc
item.Equipments = make([]uint32, 23)
item.Enchants = make([]uint32, 23)

for i, j := 0, 0; j < 23; i, j = i+2, j+1 {
for i, j := 0, 0; j < 23 && i < len(strs); i, j = i+2, j+1 {
equip, _ := strconv.Atoi(strs[i])
item.Equipments[j] = uint32(equip)

Expand Down Expand Up @@ -139,7 +104,7 @@ func (c CharactersMYSQL) CharacterToLogInByGUID(ctx context.Context, realmID uin
item.Equipments = make([]uint32, 23)
item.Enchants = make([]uint32, 23)

for i, j := 0, 0; j < 23; i, j = i+2, j+1 {
for i, j := 0, 0; j < 23 && i < len(strs); i, j = i+2, j+1 {
equip, _ := strconv.Atoi(strs[i])
item.Equipments[j] = uint32(equip)

Expand Down
77 changes: 77 additions & 0 deletions apps/charserver/repo/characters_stmt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package repo

import (
"fmt"

shrepo "github.com/walkline/ToCloud9/shared/repo"
)

type CharactersPreparedStatements uint32

const (
StmtListCharactersToLogin CharactersPreparedStatements = iota
StmtCharacterToLogin
StmtSelectAccountData
StmtSelectCharacterWithName
)

func (s CharactersPreparedStatements) ID() uint32 {
return uint32(s)
}

func (s CharactersPreparedStatements) SchemeStatement(schemaType shrepo.SupportedSchemaType) shrepo.PreparedStatement {
switch schemaType {
case shrepo.SupportedSchemaTypeTrinityCore, shrepo.SupportedSchemaTypeAzerothCore:
return shrepo.NewGenericPreparedStatement(s.ID(), s.tcAndAcScheme())
case shrepo.SupportedSchemaTypeCMaNGOS:
return shrepo.NewGenericPreparedStatement(s.ID(), s.cmangosScheme())
}

panic(fmt.Errorf("unk scheme %s", schemaType))
}

func (s CharactersPreparedStatements) tcAndAcScheme() string {
switch s {
case StmtListCharactersToLogin:
return `SELECT c.guid, c.account, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z,
IFNULL(gm.guildid, 0), c.playerFlags, c.at_login, IFNULL(cp.entry, 0), IFNULL(cp.modelid, 0), IFNULL(cp.level, 0), c.equipmentCache, IFNULL(cb.guid, 0)
FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid
LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY c.guid`
case StmtCharacterToLogin:
return `SELECT c.guid, c.account, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z,
IFNULL(gm.guildid, 0), c.playerFlags, c.at_login, IFNULL(cp.entry, 0), IFNULL(cp.modelid, 0), IFNULL(cp.level, 0), c.equipmentCache, IFNULL(cb.guid, 0)
FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid
LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.guid = ? AND c.deleteInfos_Name IS NULL`
case StmtSelectAccountData:
return "SELECT type, time, data FROM account_data WHERE accountId = ?"
case StmtSelectCharacterWithName:
return "SELECT c.guid, account, race, class, gender, level, zone, map, position_x, position_y, position_z, IFNULL(gm.guildid, 0) FROM characters AS c LEFT JOIN guild_member AS gm ON c.guid = gm.guid WHERE name = ?"
}

panic(fmt.Errorf("unk stmt %d", s))
}

func (s CharactersPreparedStatements) cmangosScheme() string {
switch s {
case StmtListCharactersToLogin:
return `SELECT c.guid, c.account, c.name, c.race, c.class, c.gender,
(c.playerBytes & 255) AS skin, ((c.playerBytes >> 8) & 255) AS face, ((c.playerBytes >> 16) & 255) AS hairStyle,
((c.playerBytes >> 24) & 255) AS hairColor, (c.playerBytes2 & 255) AS facialStyle,
c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, IFNULL(gm.guildid, 0), c.playerFlags, c.at_login, IFNULL(cp.entry, 0), IFNULL(cp.modelid, 0), IFNULL(cp.level, 0), c.equipmentCache, 0
FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid
WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY c.guid`
case StmtCharacterToLogin:
return `SELECT c.guid, c.account, c.name, c.race, c.class, c.gender,
(c.playerBytes & 255) AS skin, ((c.playerBytes >> 8) & 255) AS face, ((c.playerBytes >> 16) & 255) AS hairStyle,
((c.playerBytes >> 24) & 255) AS hairColor, (c.playerBytes2 & 255) AS facialStyle,
c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, IFNULL(gm.guildid, 0), c.playerFlags, c.at_login, IFNULL(cp.entry, 0), IFNULL(cp.modelid, 0), IFNULL(cp.level, 0), c.equipmentCache, 0
FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid
WHERE c.guid = ? AND c.deleteInfos_Name IS NULL`
case StmtSelectAccountData:
return "SELECT type, time, data FROM account_data WHERE account = ?"
case StmtSelectCharacterWithName:
return "SELECT c.guid, account, race, class, gender, level, zone, map, position_x, position_y, position_z, IFNULL(gm.guildid, 0) FROM characters AS c LEFT JOIN guild_member AS gm ON c.guid = gm.guid WHERE name = ?"
}

panic(fmt.Errorf("unk stmt %d", s))
}
2 changes: 1 addition & 1 deletion apps/game-load-balancer/cmd/game-load-balancer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func main() {

//configureDBConn(authDB)

accountRepo, err := repo.NewAccountMySQLRepo(authDB, repo.StatementsBuilderForSchema(sharedRepo.ParseSchemaType(conf.DBSchemaType)))
accountRepo, err := repo.NewAccountMySQLRepo(authDB, repo.StatementsBuilderForSchema(sharedRepo.ParseSchemaType(conf.DBSchemaType)), sharedRepo.ParseSchemaType(conf.DBSchemaType))
if err != nil {
log.Fatal().Err(err).Msg("can't create account repo")
}
Expand Down
15 changes: 14 additions & 1 deletion apps/game-load-balancer/repo/account_mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@ package repo
import (
"context"
"database/sql"

shrepo "github.com/walkline/ToCloud9/shared/repo"
"github.com/walkline/ToCloud9/shared/slices"
)

type accountMySQLRepo struct {
db *sql.DB

accountByUserStmt *sql.Stmt

schemaType shrepo.SupportedSchemaType
}

func NewAccountMySQLRepo(db *sql.DB, stmtBuilder StatementsBuilder) (AccountRepo, error) {
func NewAccountMySQLRepo(db *sql.DB, stmtBuilder StatementsBuilder, schemaType shrepo.SupportedSchemaType) (AccountRepo, error) {
accountByUserStmt, err := db.Prepare(stmtBuilder.StmtForType(AuthStmtTypeGetAccountByUsername))
if err != nil {
return nil, err
Expand All @@ -20,6 +25,7 @@ func NewAccountMySQLRepo(db *sql.DB, stmtBuilder StatementsBuilder) (AccountRepo
return &accountMySQLRepo{
db: db,
accountByUserStmt: accountByUserStmt,
schemaType: schemaType,
}, nil
}

Expand All @@ -33,5 +39,12 @@ func (r *accountMySQLRepo) AccountByUserName(ctx context.Context, username strin
}
return nil, err
}

if r.schemaType == shrepo.SupportedSchemaTypeCMaNGOS {
slices.ReverseBytes(account.Salt)
slices.ReverseBytes(account.Verifier)
slices.ReverseBytes(account.SessionKeyAuth)
}

return account, nil
}
2 changes: 2 additions & 0 deletions apps/game-load-balancer/repo/account_stmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ func StatementsBuilderForSchema(schemaType repo.SupportedSchemaType) StatementsB
return trinityCoreStatementsBuilder{}
case repo.SupportedSchemaTypeAzerothCore:
return azerothCoreStatementsBuilder{}
case repo.SupportedSchemaTypeCMaNGOS:
return cmangosStatementsBuilder{}
}
panic("unk schema type")
}
16 changes: 16 additions & 0 deletions apps/game-load-balancer/repo/account_stmt_cm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package repo

import (
"fmt"
)

type cmangosStatementsBuilder struct{}

func (b cmangosStatementsBuilder) StmtForType(t AuthStmtType) string {
switch t {
case AuthStmtTypeGetAccountByUsername:
return "SELECT id, username, UNHEX(s), UNHEX(v), UNHEX(sessionkey), locked, IFNULL(last_ip, '') FROM account WHERE username = ?"
}

panic(fmt.Sprintf("unk stmt type %d", uint8(t)))
}
Loading

0 comments on commit fd906b5

Please sign in to comment.