Create account, chr tables & save chr to Postgres
This commit is contained in:
@@ -8461,7 +8461,7 @@ public class Character extends AbstractCharacterObject {
|
||||
ps.setInt(21, stats.mapId());
|
||||
ps.setInt(22, stats.meso());
|
||||
ps.setInt(23, stats.hpMpApUsed());
|
||||
ps.setInt(24, stats.spawnPoint());
|
||||
ps.setInt(24, stats.spawnPortal());
|
||||
ps.setInt(25, stats.party());
|
||||
ps.setInt(26, stats.buddyCapacity());
|
||||
ps.setInt(27, stats.messenger());
|
||||
@@ -8515,7 +8515,7 @@ public class Character extends AbstractCharacterObject {
|
||||
.meso(meso.get())
|
||||
.hpMpApUsed(hpMpApUsed)
|
||||
.mapId(getSaveMap())
|
||||
.spawnPoint(getSaveSpawnpoint())
|
||||
.spawnPortal(getSaveSpawnPortal())
|
||||
.buddyCapacity(buddylist.getCapacity())
|
||||
.monsterBookCover(bookCover)
|
||||
.dojoVanquisherStage(vanquisherStage)
|
||||
@@ -8607,7 +8607,7 @@ public class Character extends AbstractCharacterObject {
|
||||
return map.getId();
|
||||
}
|
||||
|
||||
private int getSaveSpawnpoint() {
|
||||
private int getSaveSpawnPortal() {
|
||||
if (map == null || map.getId() == MapId.CRIMSONWOOD_VALLEY_1 || map.getId() == MapId.CRIMSONWOOD_VALLEY_2) { // TODO: clean up. Shouldn't hardcode these maps.
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public record CharacterStats(
|
||||
int mapId,
|
||||
int meso,
|
||||
int hpMpApUsed,
|
||||
int spawnPoint,
|
||||
int spawnPortal,
|
||||
int party,
|
||||
int buddyCapacity,
|
||||
int messenger,
|
||||
|
||||
@@ -20,6 +20,7 @@ public class ServerConfig {
|
||||
public String PG_DB_ADMIN_PASSWORD;
|
||||
public String PG_DB_USERNAME;
|
||||
public String PG_DB_PASSWORD;
|
||||
public boolean PG_DB_CLEAN;
|
||||
|
||||
//Login Configuration
|
||||
public int WORLDS;
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package database;
|
||||
|
||||
import lombok.Builder;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Builder
|
||||
public record PgDatabaseConfig(
|
||||
String databaseName, String host, String schema,
|
||||
String adminUsername, String adminPassword,
|
||||
String username, String password,
|
||||
Duration poolInitTimeout
|
||||
Duration poolInitTimeout,
|
||||
boolean clean
|
||||
) {
|
||||
public PgDatabaseConfig {
|
||||
verifyNotBlank(databaseName);
|
||||
|
||||
99
src/main/java/database/character/CharacterRepository.java
Normal file
99
src/main/java/database/character/CharacterRepository.java
Normal file
@@ -0,0 +1,99 @@
|
||||
package database.character;
|
||||
|
||||
import client.CharacterStats;
|
||||
import org.jdbi.v3.core.Handle;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
public class CharacterRepository {
|
||||
|
||||
public boolean update(Handle handle, CharacterStats stats) {
|
||||
String sql = """
|
||||
UPDATE chr
|
||||
SET level = :level, exp = :exp, str = :str, dex = :dex, "int" = :int, luk = :luk, hp = :hp, mp = :mp,
|
||||
max_hp = :max_hp, max_mp = :max_mp, ap = :ap, sp = :sp, job = :job, fame = :fame, gender = :gender,
|
||||
skin = :skin, hair = :hair, face = :face, meso = :meso, map_id = :map_id, spawn_portal = :spawn_portal,
|
||||
gacha_exp = :gacha_exp, used_hp_mp_ap = :used_hp_mp_ap, gm_level = :gm_level, party_id = :party_id,
|
||||
buddy_capacity = :buddy_capacity, messenger_id = :messenger_id,
|
||||
messenger_position = :messenger_position, mount_level = :mount_level, mount_exp = :mount_exp,
|
||||
mount_tiredness = :mount_tiredness, omok_wins = :omok_wins, omok_losses = :omok_losses,
|
||||
omok_ties = :omok_ties, matchcard_wins = :matchcard_wins, matchcard_losses = :matchcard_losses,
|
||||
matchcard_ties = :matchcard_ties, equip_slots = :equip_slots, use_slots = :use_slots,
|
||||
setup_slots = :setup_slots, etc_slots = :etc_slots, monster_book_cover = :monster_book_cover,
|
||||
dojo_tutorial_complete = :dojo_tutorial_complete, dojo_points = :dojo_points,
|
||||
dojo_last_stage = :dojo_last_stage, dojo_vanquisher_stage = :dojo_vanquisher_stage,
|
||||
dojo_vanquisher_kills = :dojo_vanquisher_kills, ariant_points = :ariant_points,
|
||||
data_string = :data_string, party_search = :party_search, jail_expire = :jail_expire,
|
||||
last_exp_gain = :last_exp_gain, partner_id = :partner_id, marriage_item_id = :marriage_item_id,
|
||||
updated_at = now()
|
||||
WHERE id = :id""";
|
||||
|
||||
int updatedRows = handle.createUpdate(sql)
|
||||
.bind("level", stats.level())
|
||||
.bind("exp", stats.exp())
|
||||
.bind("str", stats.str())
|
||||
.bind("dex", stats.dex())
|
||||
.bind("int", stats.int_())
|
||||
.bind("luk", stats.luk())
|
||||
.bind("hp", stats.hp())
|
||||
.bind("mp", stats.mp())
|
||||
.bind("max_hp", stats.maxHp())
|
||||
.bind("max_mp", stats.maxMp())
|
||||
.bind("ap", stats.ap())
|
||||
.bind("sp", parseMultiSkillbookSp(stats.sp()))
|
||||
.bind("job", stats.job())
|
||||
.bind("fame", stats.fame())
|
||||
.bind("gender", stats.gender())
|
||||
.bind("skin", stats.skin())
|
||||
.bind("hair", stats.hair())
|
||||
.bind("face", stats.face())
|
||||
.bind("meso", stats.meso())
|
||||
.bind("map_id", stats.mapId())
|
||||
.bind("spawn_portal", stats.spawnPortal())
|
||||
.bind("gacha_exp", stats.gachaExp())
|
||||
.bind("used_hp_mp_ap", stats.hpMpApUsed())
|
||||
.bind("gm_level", stats.gmLevel())
|
||||
.bind("party_id", stats.party())
|
||||
.bind("buddy_capacity", stats.buddyCapacity())
|
||||
.bind("messenger_id", stats.messenger())
|
||||
.bind("messenger_position", stats.messengerPosition())
|
||||
.bind("mount_level", stats.mountLevel())
|
||||
.bind("mount_exp", stats.mountExp())
|
||||
.bind("mount_tiredness", stats.mountTiredness())
|
||||
.bind("omok_wins", stats.omokWins())
|
||||
.bind("omok_losses", stats.omokLosses())
|
||||
.bind("omok_ties", stats.omokTies())
|
||||
.bind("matchcard_wins", stats.matchCardWins())
|
||||
.bind("matchcard_losses", stats.matchCardLosses())
|
||||
.bind("matchcard_ties", stats.matchCardTies())
|
||||
.bind("equip_slots", stats.equipSlots())
|
||||
.bind("use_slots", stats.useSlots())
|
||||
.bind("setup_slots", stats.setupSlots())
|
||||
.bind("etc_slots", stats.etcSlots())
|
||||
.bind("monster_book_cover", stats.monsterBookCover())
|
||||
.bind("dojo_tutorial_complete", stats.dojoTutorialComplete())
|
||||
.bind("dojo_points", stats.dojoPoints())
|
||||
.bind("dojo_last_stage", stats.dojoStage())
|
||||
.bind("dojo_vanquisher_stage", stats.dojoVanquisherStage())
|
||||
.bind("dojo_vanquisher_kills", stats.dojoVanquisherKills())
|
||||
.bind("ariant_points", stats.ariantPoints())
|
||||
.bind("data_string", stats.dataString())
|
||||
.bind("party_search", stats.canRecvPartySearchInvite())
|
||||
.bind("jail_expire", new Timestamp(stats.jailExpiration()))
|
||||
.bind("last_exp_gain", new Timestamp(stats.lastExpGainTime()))
|
||||
.bind("partner_id", stats.partnerId())
|
||||
.bind("marriage_item_id", stats.marriageItemId())
|
||||
.bind("id", stats.id())
|
||||
.execute();
|
||||
|
||||
return updatedRows > 0;
|
||||
}
|
||||
|
||||
private int parseMultiSkillbookSp(String sp) {
|
||||
if (sp == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Integer.parseInt(sp.split(",")[0]);
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,8 @@ package database.character;
|
||||
import client.Character;
|
||||
import database.PgDatabaseConnection;
|
||||
import database.monsterbook.MonsterCardRepository;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jdbi.v3.core.Handle;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import tools.DatabaseConnection;
|
||||
|
||||
import java.sql.Connection;
|
||||
@@ -13,14 +12,17 @@ import java.sql.SQLException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
@Slf4j
|
||||
public class CharacterSaver {
|
||||
private static final Logger log = LoggerFactory.getLogger(CharacterSaver.class);
|
||||
private final PgDatabaseConnection pgConnection;
|
||||
private final CharacterRepository characterRepository;
|
||||
private final MonsterCardRepository monsterCardRepository;
|
||||
|
||||
public CharacterSaver(PgDatabaseConnection pgConnection,
|
||||
CharacterRepository characterRepository,
|
||||
MonsterCardRepository monsterCardRepository) {
|
||||
this.pgConnection = pgConnection;
|
||||
this.characterRepository = characterRepository;
|
||||
this.monsterCardRepository = monsterCardRepository;
|
||||
}
|
||||
|
||||
@@ -61,13 +63,16 @@ public class CharacterSaver {
|
||||
|
||||
try (Handle handle = pgConnection.getHandle()) {
|
||||
handle.useTransaction(h -> doPostgresSave(h, chr));
|
||||
} catch (Exception e) {
|
||||
log.error("Error saving chr {} to PG", chr.getName(), e);
|
||||
}
|
||||
|
||||
Duration saveDuration = Duration.between(before, Instant.now());
|
||||
log.debug("Saved {} to Postgres in {} ms", chr.getName(), saveDuration.toMillis());
|
||||
log.debug("Saved {} to PostgreSQL in {} ms", chr.getName(), saveDuration.toMillis());
|
||||
}
|
||||
|
||||
private void doPostgresSave(Handle handle, Character chr) {
|
||||
characterRepository.update(handle, chr.getCharacterStats());
|
||||
monsterCardRepository.save(handle, chr.getId(), chr.getMonsterBook().getCards());
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,11 @@ public class FlywayRunner {
|
||||
"server-username", dbConfig.username(),
|
||||
"server-password", dbConfig.password())
|
||||
)
|
||||
.cleanDisabled(!dbConfig.clean())
|
||||
.load();
|
||||
if (dbConfig.clean()) {
|
||||
flyway.clean();
|
||||
}
|
||||
flyway.migrate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import constants.net.ServerConstants;
|
||||
import database.PgDatabaseConfig;
|
||||
import database.PgDatabaseConnection;
|
||||
import database.character.CharacterLoader;
|
||||
import database.character.CharacterRepository;
|
||||
import database.character.CharacterSaver;
|
||||
import database.drop.DropProvider;
|
||||
import database.drop.DropRepository;
|
||||
@@ -972,12 +973,17 @@ public class Server {
|
||||
if (pgDbHost == null) {
|
||||
pgDbHost = serverConfig.PG_DB_HOST;
|
||||
}
|
||||
return new PgDatabaseConfig(
|
||||
serverConfig.PG_DB_NAME, pgDbHost, serverConfig.PG_DB_SCHEMA,
|
||||
serverConfig.PG_DB_ADMIN_USERNAME, serverConfig.PG_DB_ADMIN_PASSWORD,
|
||||
serverConfig.PG_DB_USERNAME, serverConfig.PG_DB_PASSWORD,
|
||||
Duration.ofSeconds(serverConfig.INIT_CONNECTION_POOL_TIMEOUT)
|
||||
);
|
||||
return PgDatabaseConfig.builder()
|
||||
.databaseName(serverConfig.PG_DB_NAME)
|
||||
.host(pgDbHost)
|
||||
.schema(serverConfig.PG_DB_SCHEMA)
|
||||
.adminUsername(serverConfig.PG_DB_ADMIN_USERNAME)
|
||||
.adminPassword(serverConfig.PG_DB_ADMIN_PASSWORD)
|
||||
.username(serverConfig.PG_DB_USERNAME)
|
||||
.password(serverConfig.PG_DB_PASSWORD)
|
||||
.poolInitTimeout(Duration.ofSeconds(serverConfig.INIT_CONNECTION_POOL_TIMEOUT))
|
||||
.clean(serverConfig.PG_DB_CLEAN)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void runDatabaseMigration(PgDatabaseConfig config) {
|
||||
@@ -1002,9 +1008,10 @@ public class Server {
|
||||
}
|
||||
|
||||
private ChannelDependencies registerChannelDependencies(PgDatabaseConnection connection) {
|
||||
CharacterRepository characterRepository = new CharacterRepository();
|
||||
MonsterCardRepository monsterCardRepository = new MonsterCardRepository(connection);
|
||||
CharacterLoader characterLoader = new CharacterLoader(monsterCardRepository);
|
||||
CharacterSaver characterSaver = new CharacterSaver(connection, monsterCardRepository);
|
||||
CharacterSaver characterSaver = new CharacterSaver(connection, characterRepository, monsterCardRepository);
|
||||
TransitionService transitionService = new TransitionService(characterSaver);
|
||||
BanService banService = new BanService(transitionService);
|
||||
NoteService noteService = new NoteService(new NoteDao(connection));
|
||||
|
||||
30
src/main/resources/db/migration/postgresql/V0.8__account.sql
Normal file
30
src/main/resources/db/migration/postgresql/V0.8__account.sql
Normal file
@@ -0,0 +1,30 @@
|
||||
CREATE TABLE account
|
||||
(
|
||||
id serial NOT NULL,
|
||||
name varchar(30) NOT NULL,
|
||||
password varchar(200) NOT NULL,
|
||||
pin varchar(4),
|
||||
pic varchar(26),
|
||||
logged_in smallint DEFAULT 0 NOT NULL,
|
||||
created_at timestamp DEFAULT now() NOT NULL,
|
||||
last_login timestamp,
|
||||
birthday date NOT NULL,
|
||||
banned boolean DEFAULT false NOT NULL,
|
||||
banreason text,
|
||||
macs text,
|
||||
nx_credit integer DEFAULT 0 NOT NULL,
|
||||
maple_point integer DEFAULT 0 NOT NULL,
|
||||
nx_prepaid integer DEFAULT 0 NOT NULL,
|
||||
chr_slots smallint DEFAULT 3 NOT NULL,
|
||||
gender smallint DEFAULT 10 NOT NULL,
|
||||
temp_ban_timestamp timestamp,
|
||||
greason smallint,
|
||||
tos_accepted boolean DEFAULT false NOT NULL,
|
||||
ip text,
|
||||
hwid text,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE (name)
|
||||
);
|
||||
CREATE UNIQUE INDEX lower_account_name_idx ON "account" (lower(name) );
|
||||
GRANT SELECT, UPDATE ON TABLE account TO ${server-username};
|
||||
GRANT USAGE ON SEQUENCE account_id_seq TO ${server-username};
|
||||
@@ -0,0 +1,80 @@
|
||||
CREATE TABLE chr
|
||||
(
|
||||
id serial NOT NULL,
|
||||
account integer NOT NULL,
|
||||
world integer NOT NULL,
|
||||
name varchar(12) NOT NULL,
|
||||
level smallint NOT NULL,
|
||||
exp integer NOT NULL,
|
||||
str smallint NOT NULL,
|
||||
dex smallint NOT NULL,
|
||||
"int" smallint NOT NULL,
|
||||
luk smallint NOT NULL,
|
||||
hp smallint NOT NULL,
|
||||
mp smallint NOT NULL,
|
||||
max_hp smallint NOT NULL,
|
||||
max_mp smallint NOT NULL,
|
||||
ap smallint NOT NULL,
|
||||
sp smallint NOT NULL,
|
||||
job smallint NOT NULL,
|
||||
fame smallint NOT NULL,
|
||||
gender smallint NOT NULL,
|
||||
skin smallint NOT NULL,
|
||||
hair integer NOT NULL,
|
||||
face integer NOT NULL,
|
||||
meso integer DEFAULT 0 NOT NULL,
|
||||
map_id integer NOT NULL,
|
||||
spawn_portal smallint NOT NULL,
|
||||
gacha_exp integer NOT NULL,
|
||||
used_hp_mp_ap integer NOT NULL,
|
||||
gm_level smallint NOT NULL,
|
||||
party_id integer,
|
||||
buddy_capacity smallint,
|
||||
"rank" integer,
|
||||
rank_move integer,
|
||||
job_rank integer,
|
||||
job_rank_move integer,
|
||||
guild_id integer,
|
||||
guild_rank smallint,
|
||||
alliance_rank smallint,
|
||||
messenger_id integer,
|
||||
messenger_position smallint,
|
||||
mount_level smallint,
|
||||
mount_exp integer,
|
||||
mount_tiredness smallint,
|
||||
omok_wins integer DEFAULT 0 NOT NULL,
|
||||
omok_losses integer DEFAULT 0 NOT NULL,
|
||||
omok_ties integer DEFAULT 0 NOT NULL,
|
||||
matchcard_wins integer DEFAULT 0 NOT NULL,
|
||||
matchcard_losses integer DEFAULT 0 NOT NULL,
|
||||
matchcard_ties integer DEFAULT 0 NOT NULL,
|
||||
merchant_mesos integer DEFAULT 0 NOT NULL,
|
||||
has_merchant boolean DEFAULT false NOT NULL,
|
||||
equip_slots smallint NOT NULL,
|
||||
use_slots smallint NOT NULL,
|
||||
setup_slots smallint NOT NULL,
|
||||
etc_slots smallint NOT NULL,
|
||||
family_id integer,
|
||||
partner_id integer,
|
||||
marriage_item_id integer,
|
||||
monster_book_cover integer,
|
||||
dojo_tutorial_complete boolean DEFAULT false NOT NULL,
|
||||
dojo_points integer DEFAULT 0 NOT NULL,
|
||||
dojo_last_stage integer,
|
||||
dojo_vanquisher_stage integer,
|
||||
dojo_vanquisher_kills integer DEFAULT 0 NOT NULL,
|
||||
ariant_points integer DEFAULT 0 NOT NULL,
|
||||
data_string text,
|
||||
party_search boolean DEFAULT false NOT NULL,
|
||||
last_logout timestamp,
|
||||
last_exp_gain timestamp,
|
||||
jail_expire timestamp,
|
||||
created_at timestamp DEFAULT now() NOT NULL,
|
||||
updated_at timestamp DEFAULT now() NOT NULL,
|
||||
UNIQUE (name),
|
||||
PRIMARY KEY (id),
|
||||
FOREIGN KEY (account) REFERENCES account (id) ON DELETE RESTRICT
|
||||
);
|
||||
CREATE UNIQUE INDEX lower_character_name_idx ON chr (lower(name) );
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE chr TO ${server-username};
|
||||
GRANT USAGE ON SEQUENCE chr_id_seq TO ${server-username};
|
||||
Reference in New Issue
Block a user