postgres!
This commit is contained in:
		
							parent
							
								
									4989ecaa7c
								
							
						
					
					
						commit
						8ddbdda0a4
					
				| @ -15,7 +15,7 @@ mio-extras = "2.0.5" | ||||
| crc = "^1.0.0" | ||||
| bcrypt = "0.4" | ||||
| threadpool = "1.0" | ||||
| chrono = "*" | ||||
| chrono = "0.4.11" | ||||
| serde = "*" | ||||
| serde_json = "*" | ||||
| ron = "*" | ||||
| @ -29,4 +29,8 @@ thiserror = "1.0.15" | ||||
| ages-prs = "0.1" | ||||
| async-trait = "0.1.31" | ||||
| lazy_static = "1.4.0" | ||||
| barrel = { version = "0.6.5", features = ["pg"] } | ||||
| postgres = "0.17.5" | ||||
| refinery = { version = "0.3.0", features = ["postgres"] } | ||||
| sqlx = { version = "0.4.0-beta.1", features = ["postgres", "json", "chrono"] } | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| pub mod entitygateway; | ||||
| pub mod inmemory; | ||||
| pub mod postgres; | ||||
| 
 | ||||
| pub use entitygateway::EntityGateway; | ||||
| pub use inmemory::InMemoryGateway; | ||||
| pub use self::postgres::PostgresGateway; | ||||
|  | ||||
							
								
								
									
										111
									
								
								src/entity/gateway/postgres/migrations/V0001__initial.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/entity/gateway/postgres/migrations/V0001__initial.sql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,111 @@ | ||||
| create table user_accounts ( | ||||
|   id serial primary key not null, | ||||
|   name varchar(128) not null unique, | ||||
|   password varchar(128) not null, | ||||
|   banned timestamptz, | ||||
|   muted timestamptz, | ||||
|   created_at timestamptz default current_timestamp not null, | ||||
|   flags integer default 0 | ||||
| ); | ||||
| 
 | ||||
| create table user_settings ( | ||||
|   id serial primary key not null, | ||||
|   user_account integer references user_accounts (id) not null, | ||||
|   blocked_users bytea not null, | ||||
|   key_config bytea not null, | ||||
|   joystick_config bytea not null, | ||||
|   option_flags integer not null, | ||||
|   shortcuts bytea not null, | ||||
|   symbol_chats bytea not null, | ||||
|   team_name bytea not null | ||||
| ); | ||||
| 
 | ||||
| /* TODO: guild card data */ | ||||
| 
 | ||||
| create table player_character ( | ||||
|   id serial primary key not null, | ||||
|   user_account integer references user_accounts (id) not null, | ||||
|   slot smallint not null, | ||||
|   name varchar(12) not null, | ||||
|   exp integer not null, | ||||
|   class varchar(12) null, | ||||
|   section_id varchar(12) not null, | ||||
| 
 | ||||
|   costume smallint not null, | ||||
|   skin smallint not null, | ||||
|   face smallint not null, | ||||
|   head smallint not null, | ||||
|   hair smallint not null, | ||||
|   hair_r smallint not null, | ||||
|   hair_g smallint not null, | ||||
|   hair_b smallint not null, | ||||
|   prop_x real not null, | ||||
|   prop_y real not null, | ||||
|    | ||||
|   techs bytea not null, | ||||
|   | ||||
|   config bytea not null, | ||||
|   infoboard varchar(172) not null, | ||||
|   guildcard varchar(172) not null, | ||||
|    | ||||
|   power smallint not null, | ||||
|   mind smallint not null, | ||||
|   def smallint not null, | ||||
|   evade smallint not null, | ||||
|   luck smallint not null, | ||||
|   hp smallint not null, | ||||
|   tp smallint not null, | ||||
| 
 | ||||
|   tech_menu bytea not null, | ||||
|   meseta integer not null, | ||||
|   bank_meseta integer not null | ||||
| 
 | ||||
| ); | ||||
| 
 | ||||
| 
 | ||||
| create table item ( | ||||
|   id serial primary key not null, | ||||
|   item jsonb not null | ||||
| ); | ||||
| 
 | ||||
| create table item_location ( | ||||
|   item integer references item (id) not null, | ||||
|   location jsonb not null, | ||||
|   created_at timestamptz default current_timestamp not null | ||||
| ); | ||||
| 
 | ||||
| create table weapon_modifier ( | ||||
|   weapon integer references item (id) not null, | ||||
|   modifier jsonb not null, | ||||
|   created_at timestamptz default current_timestamp not null | ||||
| ); | ||||
| 
 | ||||
| create table armor_modifier ( | ||||
|   armor integer references item (id) not null, | ||||
|   modifier jsonb not null, | ||||
|   created_at timestamptz default current_timestamp not null | ||||
| ); | ||||
| 
 | ||||
| create table shield_modifier ( | ||||
|   shield integer references item (id) not null, | ||||
|   modifier jsonb not null, | ||||
|   created_at timestamptz default current_timestamp not null | ||||
| ); | ||||
| 
 | ||||
| create table unit_modifier ( | ||||
|   unit integer references item (id) not null, | ||||
|   modifier jsonb not null, | ||||
|   created_at timestamptz default current_timestamp not null | ||||
| ); | ||||
| 
 | ||||
| create table esweapon_modifier ( | ||||
|   esweapon integer references item (id) not null, | ||||
|   modifier jsonb not null, | ||||
|   created_at timestamptz default current_timestamp not null | ||||
| ); | ||||
| 
 | ||||
| create table mag_modifier ( | ||||
|   mag integer references item (id) not null, | ||||
|   modifier jsonb not null, | ||||
|   created_at timestamptz default current_timestamp not null | ||||
| ); | ||||
							
								
								
									
										3
									
								
								src/entity/gateway/postgres/migrations/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/entity/gateway/postgres/migrations/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| use refinery::include_migration_mods; | ||||
| 
 | ||||
| include_migration_mods!("src/entity/gateway/postgres/migrations"); | ||||
							
								
								
									
										5
									
								
								src/entity/gateway/postgres/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/entity/gateway/postgres/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| pub mod postgres; | ||||
| pub mod migrations; | ||||
| pub mod models; | ||||
| 
 | ||||
| pub use self::postgres::PostgresGateway; | ||||
							
								
								
									
										688
									
								
								src/entity/gateway/postgres/models.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										688
									
								
								src/entity/gateway/postgres/models.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,688 @@ | ||||
| use std::collections::HashMap; | ||||
| use std::convert::Into; | ||||
| use serde::{Serialize, Deserialize}; | ||||
| use futures::TryStreamExt; | ||||
| use libpso::character::{settings, guildcard}; | ||||
| use libpso::util::vec_to_array; | ||||
| use crate::entity::account::*; | ||||
| use crate::entity::character::*; | ||||
| use crate::entity::gateway::EntityGateway; | ||||
| use crate::entity::item::*; | ||||
| use crate::ship::map::MapArea; | ||||
| 
 | ||||
| use sqlx::postgres::PgPoolOptions; | ||||
| use sqlx::Row; | ||||
| use sqlx::Execute; | ||||
| use postgres::{Client, NoTls}; | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Debug, sqlx::FromRow)] | ||||
| pub struct PgUserAccount { | ||||
|     id: i32, | ||||
|     name: String, | ||||
|     password: String, | ||||
|     banned: Option<chrono::DateTime<chrono::Utc>>, | ||||
|     muted: Option<chrono::DateTime<chrono::Utc>>, | ||||
|     created_at: chrono::DateTime<chrono::Utc>, | ||||
|     flags: i32, | ||||
| } | ||||
| 
 | ||||
| impl Into<UserAccountEntity> for PgUserAccount { | ||||
|     fn into(self) -> UserAccountEntity { | ||||
|         UserAccountEntity { | ||||
|             id: UserAccountId(self.id as u32), | ||||
|             username: self.name, | ||||
|             password: self.password, | ||||
|             banned_until: self.banned, | ||||
|             muted_until: self.muted, | ||||
|             created_at: self.created_at, | ||||
|             flags: self.flags as u32, | ||||
|             // TOOD
 | ||||
|             guildcard: self.id as u32 + 1, | ||||
|             team_id: None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, sqlx::FromRow)] | ||||
| pub struct PgUserSettings { | ||||
|     id: i32, | ||||
|     user_account: i32, | ||||
|     blocked_users: Vec<u8>, //[u32; 0x1E],
 | ||||
|     key_config: Vec<u8>, //[u8; 0x16C],
 | ||||
|     joystick_config: Vec<u8>, //[u8; 0x38],
 | ||||
|     option_flags: i32, | ||||
|     shortcuts: Vec<u8>, //[u8; 0xA40],
 | ||||
|     symbol_chats: Vec<u8>, //[u8; 0x4E0],
 | ||||
|     team_name: Vec<u8>, //[u16; 0x10],
 | ||||
| } | ||||
| 
 | ||||
| impl Into<UserSettingsEntity> for PgUserSettings { | ||||
|     fn into(self) -> UserSettingsEntity { | ||||
|         UserSettingsEntity { | ||||
|             id: UserSettingsId(self.id as u32), | ||||
|             user_id: UserAccountId(self.user_account as u32), | ||||
|             settings: settings::UserSettings { | ||||
|                 blocked_users: vec_to_array(self.blocked_users.chunks(4).map(|b| u32::from_le_bytes([b[0], b[1], b[2], b[3]])).collect()), | ||||
|                 key_config: vec_to_array(self.key_config), | ||||
|                 joystick_config: vec_to_array(self.joystick_config), | ||||
|                 option_flags: self.option_flags as u32, | ||||
|                 shortcuts: vec_to_array(self.shortcuts), | ||||
|                 symbol_chats: vec_to_array(self.symbol_chats), | ||||
|                 team_name: vec_to_array(self.team_name.chunks(2).map(|b| u16::from_le_bytes([b[0], b[1]])).collect()), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(sqlx::Type, Debug)] | ||||
| #[sqlx(rename_all = "lowercase")] | ||||
| pub enum PgCharacterClass { | ||||
|     HUmar, | ||||
|     HUnewearl, | ||||
|     HUcast, | ||||
|     HUcaseal, | ||||
|     RAmar, | ||||
|     RAmarl, | ||||
|     RAcast, | ||||
|     RAcaseal, | ||||
|     FOmar, | ||||
|     FOmarl, | ||||
|     FOnewm, | ||||
|     FOnewearl, | ||||
| } | ||||
| 
 | ||||
| impl Into<CharacterClass> for PgCharacterClass { | ||||
|     fn into(self) -> CharacterClass { | ||||
|         match self { | ||||
|             PgCharacterClass::HUmar => CharacterClass::HUmar, | ||||
|             PgCharacterClass::HUnewearl => CharacterClass::HUnewearl, | ||||
|             PgCharacterClass::HUcast => CharacterClass::HUcast, | ||||
|             PgCharacterClass::HUcaseal => CharacterClass::HUcaseal, | ||||
|             PgCharacterClass::RAmar => CharacterClass::RAmar, | ||||
|             PgCharacterClass::RAmarl => CharacterClass::RAmarl, | ||||
|             PgCharacterClass::RAcast => CharacterClass::RAcast, | ||||
|             PgCharacterClass::RAcaseal => CharacterClass::RAcaseal, | ||||
|             PgCharacterClass::FOmar => CharacterClass::FOmar, | ||||
|             PgCharacterClass::FOmarl => CharacterClass::FOmarl, | ||||
|             PgCharacterClass::FOnewm => CharacterClass::FOnewm, | ||||
|             PgCharacterClass::FOnewearl => CharacterClass::FOnewearl, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<CharacterClass> for PgCharacterClass { | ||||
|     fn from(other: CharacterClass) -> PgCharacterClass { | ||||
|         match other { | ||||
|             CharacterClass::HUmar => PgCharacterClass::HUmar, | ||||
|             CharacterClass::HUnewearl => PgCharacterClass::HUnewearl, | ||||
|             CharacterClass::HUcast => PgCharacterClass::HUcast, | ||||
|             CharacterClass::HUcaseal => PgCharacterClass::HUcaseal, | ||||
|             CharacterClass::RAmar => PgCharacterClass::RAmar, | ||||
|             CharacterClass::RAmarl => PgCharacterClass::RAmarl, | ||||
|             CharacterClass::RAcast => PgCharacterClass::RAcast, | ||||
|             CharacterClass::RAcaseal => PgCharacterClass::RAcaseal, | ||||
|             CharacterClass::FOmar => PgCharacterClass::FOmar, | ||||
|             CharacterClass::FOmarl => PgCharacterClass::FOmarl, | ||||
|             CharacterClass::FOnewm => PgCharacterClass::FOnewm, | ||||
|             CharacterClass::FOnewearl => PgCharacterClass::FOnewearl, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(sqlx::Type, Debug)] | ||||
| #[sqlx(rename_all = "lowercase")] | ||||
| pub enum PgSectionId { | ||||
|     Viridia, | ||||
|     Greenill, | ||||
|     Skyly, | ||||
|     Bluefull, | ||||
|     Purplenum, | ||||
|     Pinkal, | ||||
|     Redria, | ||||
|     Oran, | ||||
|     Yellowboze, | ||||
|     Whitill, | ||||
| } | ||||
| 
 | ||||
| impl Into<SectionID> for PgSectionId { | ||||
|     fn into(self) -> SectionID { | ||||
|         match self { | ||||
|             PgSectionId::Viridia => SectionID::Viridia, | ||||
|             PgSectionId::Greenill => SectionID::Greenill, | ||||
|             PgSectionId::Skyly => SectionID::Skyly, | ||||
|             PgSectionId::Bluefull => SectionID::Bluefull, | ||||
|             PgSectionId::Purplenum => SectionID::Purplenum, | ||||
|             PgSectionId::Pinkal => SectionID::Pinkal, | ||||
|             PgSectionId::Redria => SectionID::Redria, | ||||
|             PgSectionId::Oran => SectionID::Oran, | ||||
|             PgSectionId::Yellowboze => SectionID::Yellowboze, | ||||
|             PgSectionId::Whitill => SectionID::Whitill, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<SectionID> for PgSectionId { | ||||
|     fn from(other: SectionID) -> PgSectionId { | ||||
|         match other { | ||||
|             SectionID::Viridia => PgSectionId::Viridia, | ||||
|             SectionID::Greenill => PgSectionId::Greenill, | ||||
|             SectionID::Skyly => PgSectionId::Skyly, | ||||
|             SectionID::Bluefull => PgSectionId::Bluefull, | ||||
|             SectionID::Purplenum => PgSectionId::Purplenum, | ||||
|             SectionID::Pinkal => PgSectionId::Pinkal, | ||||
|             SectionID::Redria => PgSectionId::Redria, | ||||
|             SectionID::Oran => PgSectionId::Oran, | ||||
|             SectionID::Yellowboze => PgSectionId::Yellowboze, | ||||
|             SectionID::Whitill => PgSectionId::Whitill, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Debug, sqlx::FromRow)] | ||||
| pub struct PgCharacter { | ||||
|     id: i32, | ||||
|     user_account: i32, | ||||
|     pub slot: i16, | ||||
|     name: String, | ||||
|     exp: i32, | ||||
|     class: String, | ||||
|     section_id: String, | ||||
| 
 | ||||
|     costume: i16, | ||||
|     skin: i16, | ||||
|     face: i16, | ||||
|     head: i16, | ||||
|     hair: i16, | ||||
|     hair_r: i16, | ||||
|     hair_g: i16, | ||||
|     hair_b: i16, | ||||
|     prop_x: f32, | ||||
|     prop_y: f32, | ||||
| 
 | ||||
|     techs: Vec<u8>, | ||||
|     
 | ||||
|     config: Vec<u8>, | ||||
|     infoboard: String, | ||||
|     guildcard: String, | ||||
|     
 | ||||
|     power: i16, | ||||
|     mind: i16, | ||||
|     def: i16, | ||||
|     evade: i16, | ||||
|     luck: i16, | ||||
|     hp: i16, | ||||
|     tp: i16, | ||||
| 
 | ||||
|     tech_menu: Vec<u8>, | ||||
|     meseta: i32, | ||||
|     bank_meseta: i32, | ||||
| } | ||||
| 
 | ||||
| impl Into<CharacterEntity> for PgCharacter { | ||||
|     fn into(self) -> CharacterEntity { | ||||
|         CharacterEntity { | ||||
|             id: CharacterEntityId(self.id as u32), | ||||
|             user_id: UserAccountId(self.user_account as u32), | ||||
|             slot: self.slot as u32, | ||||
|             name: self.name, | ||||
|             exp: self.exp as u32, | ||||
|             char_class: self.class.parse().unwrap(), | ||||
|             section_id: self.section_id.parse().unwrap(), | ||||
|             appearance: CharacterAppearance { | ||||
|                 costume: self.costume as u16, | ||||
|                 skin: self.skin as u16, | ||||
|                 face: self.face as u16, | ||||
|                 head: self.head as u16, | ||||
|                 hair: self.hair as u16, | ||||
|                 hair_r: self.hair_r as u16, | ||||
|                 hair_g: self.hair_g as u16, | ||||
|                 hair_b: self.hair_b as u16, | ||||
|                 prop_x: self.prop_x, | ||||
|                 prop_y: self.prop_y, | ||||
|             }, | ||||
|             techs: CharacterTechniques { | ||||
|                 techs: self.techs.iter().enumerate().take(19).map(|(i, t)| (tech::Technique::from_value(i as u8), TechLevel(*t)) ).collect() | ||||
|             }, | ||||
|             config: CharacterConfig { | ||||
|                 raw_data: vec_to_array(self.config) | ||||
|             }, | ||||
|             info_board: CharacterInfoboard { | ||||
|                 board: libpso::utf8_to_utf16_array!(self.infoboard, 172), | ||||
|             }, | ||||
|             guildcard: CharacterGuildCard { | ||||
|                 description: self.guildcard, | ||||
|             }, | ||||
|             materials: CharacterMaterials { | ||||
|                 power: self.power as u32, | ||||
|                 mind: self.mind as u32, | ||||
|                 def: self.def as u32, | ||||
|                 evade: self.evade as u32, | ||||
|                 luck: self.luck as u32, | ||||
|                 hp: self.hp as u32, | ||||
|                 tp: self.tp as u32, | ||||
|             }, | ||||
|             tech_menu: CharacterTechMenu { | ||||
|                 tech_menu: vec_to_array(self.tech_menu) | ||||
|             }, | ||||
|             meseta: self.meseta as u32, | ||||
|             bank_meseta: self.bank_meseta as u32, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, sqlx::FromRow)] | ||||
| pub struct PgGuildCard { | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct PgWeapon { | ||||
|     weapon: weapon::WeaponType, | ||||
|     special: Option<weapon::WeaponSpecial>, | ||||
|     grind: u8, | ||||
|     attrs: HashMap<weapon::Attribute, i8>, | ||||
|     tekked: bool, | ||||
| } | ||||
| 
 | ||||
| impl From<weapon::Weapon> for PgWeapon { | ||||
|     fn from(other: weapon::Weapon) -> PgWeapon { | ||||
|         PgWeapon { | ||||
|             weapon: other.weapon, | ||||
|             special: other.special, | ||||
|             grind: other.grind, | ||||
|             attrs: other.attrs.iter().flatten().map(|attr| (attr.attr, attr.value)).collect(), | ||||
|             tekked: other.tekked, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Into<weapon::Weapon> for PgWeapon { | ||||
|     fn into(self) -> weapon::Weapon { | ||||
|         let mut attrs: [Option<weapon::WeaponAttribute>; 3] = [None; 3]; | ||||
|         for (attr, (atype, value)) in attrs.iter_mut().zip(self.attrs.iter()) { | ||||
|             *attr = Some(weapon::WeaponAttribute { | ||||
|                 attr: *atype, | ||||
|                 value: *value | ||||
|             }); | ||||
|         } | ||||
|         
 | ||||
|         weapon::Weapon { | ||||
|             weapon: self.weapon, | ||||
|             special: self.special, | ||||
|             grind: self.grind, | ||||
|             attrs: attrs, | ||||
|             tekked: self.tekked, | ||||
|             modifiers: Vec::new(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct PgArmor { | ||||
|     armor: armor::ArmorType, | ||||
|     dfp: u8, | ||||
|     evp: u8, | ||||
|     slots: u8, | ||||
| } | ||||
| 
 | ||||
| impl From<armor::Armor> for PgArmor { | ||||
|     fn from(other: armor::Armor) -> PgArmor { | ||||
|         PgArmor { | ||||
|             armor: other.armor, | ||||
|             dfp: other.dfp, | ||||
|             evp: other.evp, | ||||
|             slots: other.slots, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Into<armor::Armor> for PgArmor { | ||||
|     fn into(self) -> armor::Armor { | ||||
|         armor::Armor { | ||||
|             armor: self.armor, | ||||
|             dfp: self.dfp, | ||||
|             evp: self.evp, | ||||
|             slots: self.slots, | ||||
|             modifiers: Vec::new(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct PgShield { | ||||
|     shield: shield::ShieldType, | ||||
|     dfp: u8, | ||||
|     evp: u8, | ||||
| } | ||||
| 
 | ||||
| impl From<shield::Shield> for PgShield { | ||||
|     fn from(other: shield::Shield) -> PgShield { | ||||
|         PgShield { | ||||
|             shield: other.shield, | ||||
|             dfp: other.dfp, | ||||
|             evp: other.evp, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Into<shield::Shield> for PgShield { | ||||
|     fn into(self) -> shield::Shield { | ||||
|         shield::Shield { | ||||
|             shield: self.shield, | ||||
|             dfp: self.dfp, | ||||
|             evp: self.evp, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct PgUnit { | ||||
|     unit: unit::UnitType, | ||||
|     modifier: Option<unit::UnitModifier>, | ||||
| } | ||||
| 
 | ||||
| impl From<unit::Unit> for PgUnit { | ||||
|     fn from(other: unit::Unit) -> PgUnit { | ||||
|         PgUnit { | ||||
|             unit: other.unit, | ||||
|             modifier: other.modifier, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Into<unit::Unit> for PgUnit { | ||||
|     fn into(self) -> unit::Unit { | ||||
|         unit::Unit { | ||||
|             unit: self.unit, | ||||
|             modifier: self.modifier, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct PgTool { | ||||
|     pub tool: tool::ToolType, | ||||
| } | ||||
| 
 | ||||
| impl From<tool::Tool> for PgTool { | ||||
|     fn from(other: tool::Tool) -> PgTool { | ||||
|         PgTool { | ||||
|             tool: other.tool, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Into<tool::Tool> for PgTool { | ||||
|     fn into(self) -> tool::Tool { | ||||
|         tool::Tool { | ||||
|             tool: self.tool, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct PgTechDisk { | ||||
|     tech: tech::Technique, | ||||
|     level: u32, | ||||
| } | ||||
| 
 | ||||
| impl From<tech::TechniqueDisk> for PgTechDisk { | ||||
|     fn from(other: tech::TechniqueDisk) -> PgTechDisk { | ||||
|         PgTechDisk { | ||||
|             tech: other.tech, | ||||
|             level: other.level, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Into<tech::TechniqueDisk> for PgTechDisk { | ||||
|     fn into(self) -> tech::TechniqueDisk { | ||||
|         tech::TechniqueDisk { | ||||
|             tech: self.tech, | ||||
|             level: self.level | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct PgMag { | ||||
|     mag: mag::MagType, | ||||
|     synchro: u8, | ||||
|     color: u8, | ||||
| } | ||||
| 
 | ||||
| impl From<mag::Mag> for PgMag { | ||||
|     fn from(other: mag::Mag) -> PgMag { | ||||
|         PgMag { | ||||
|             mag: other.mag, | ||||
|             synchro: other.synchro, | ||||
|             color: other.color, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Into<mag::Mag> for PgMag { | ||||
|     fn into(self) -> mag::Mag { | ||||
|         /*mag::Mag {
 | ||||
|             mag: self.mag, | ||||
|             synchro: self.synchro, | ||||
|             color: self.color, | ||||
|             def: 500, | ||||
|             pow: 0, | ||||
|             dex: 0, | ||||
|             mnd: 0, | ||||
|             iq: 0, | ||||
|             photon_blast: [None; 3], | ||||
|             class: CharacterClass::HUmar, | ||||
|             id: SectionID::Viridia, | ||||
|     }*/ | ||||
|         let mut mag = mag::Mag::baby_mag(self.color as u16); | ||||
|         mag.mag = self.mag; | ||||
|         mag.synchro = self.synchro; | ||||
|         mag | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct PgESWeapon { | ||||
|     esweapon: esweapon::ESWeaponType, | ||||
|     special: Option<esweapon::ESWeaponSpecial>, | ||||
|     name: String, | ||||
|     grind: u8, | ||||
| } | ||||
| 
 | ||||
| impl From<esweapon::ESWeapon> for PgESWeapon { | ||||
|     fn from(other: esweapon::ESWeapon) -> PgESWeapon { | ||||
|         PgESWeapon { | ||||
|             esweapon: other.esweapon, | ||||
|             special: other.special, | ||||
|             name: other.name, | ||||
|             grind: other.grind, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Into<esweapon::ESWeapon> for PgESWeapon { | ||||
|     fn into(self) -> esweapon::ESWeapon { | ||||
|         esweapon::ESWeapon { | ||||
|             esweapon: self.esweapon, | ||||
|             special: self.special, | ||||
|             name: self.name, | ||||
|             grind: self.grind, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub enum PgItemDetail { | ||||
|     Weapon(PgWeapon), | ||||
|     Armor(PgArmor), | ||||
|     Shield(PgShield), | ||||
|     Unit(PgUnit), | ||||
|     Tool(PgTool), | ||||
|     TechDisk(PgTechDisk), | ||||
|     Mag(PgMag), | ||||
|     ESWeapon(PgESWeapon), | ||||
| } | ||||
| 
 | ||||
| impl From<ItemDetail> for PgItemDetail { | ||||
|     fn from(other: ItemDetail) -> PgItemDetail { | ||||
|         match other { | ||||
|             ItemDetail::Weapon(weapon) => PgItemDetail::Weapon(weapon.into()), | ||||
|             ItemDetail::Armor(armor) => PgItemDetail::Armor(armor.into()), | ||||
|             ItemDetail::Shield(shield) => PgItemDetail::Shield(shield.into()), | ||||
|             ItemDetail::Unit(unit) => PgItemDetail::Unit(unit.into()), | ||||
|             ItemDetail::Tool(tool) => PgItemDetail::Tool(tool.into()), | ||||
|             ItemDetail::TechniqueDisk(tech_disk) => PgItemDetail::TechDisk(tech_disk.into()), | ||||
|             ItemDetail::Mag(mag) => PgItemDetail::Mag(mag.into()), | ||||
|             ItemDetail::ESWeapon(esweapon) => PgItemDetail::ESWeapon(esweapon.into()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Into<ItemDetail> for PgItemDetail { | ||||
|     fn into(self) -> ItemDetail { | ||||
|         match self { | ||||
|             PgItemDetail::Weapon(weapon) => ItemDetail::Weapon(weapon.into()), | ||||
|             PgItemDetail::Armor(armor) => ItemDetail::Armor(armor.into()), | ||||
|             PgItemDetail::Shield(shield) => ItemDetail::Shield(shield.into()), | ||||
|             PgItemDetail::Unit(unit) => ItemDetail::Unit(unit.into()), | ||||
|             PgItemDetail::Tool(tool) => ItemDetail::Tool(tool.into()), | ||||
|             PgItemDetail::TechDisk(tech_disk) => ItemDetail::TechniqueDisk(tech_disk.into()), | ||||
|             PgItemDetail::Mag(mag) => ItemDetail::Mag(mag.into()), | ||||
|             PgItemDetail::ESWeapon(esweapon) => ItemDetail::ESWeapon(esweapon.into()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, sqlx::FromRow)] | ||||
| pub struct PgItem { | ||||
|     pub id: i32, | ||||
|     pub item: sqlx::types::Json<PgItemDetail>, | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub enum PgItemLocationDetail { | ||||
|     Inventory { | ||||
|         character_id: u32, | ||||
|         slot: usize, | ||||
|         equipped: bool, | ||||
|     }, | ||||
|     Bank { | ||||
|         character_id: u32, | ||||
|         name: String, | ||||
|     }, | ||||
|     LocalFloor { | ||||
|         character_id: u32, | ||||
|         map_area: MapArea, | ||||
|         x: f32, | ||||
|         y: f32, | ||||
|         z: f32, | ||||
|     }, | ||||
|     SharedFloor { | ||||
|         map_area: MapArea, | ||||
|         x: f32, | ||||
|         y: f32, | ||||
|         z: f32, | ||||
|     }, | ||||
|     Consumed, | ||||
|     FedToMag { | ||||
|         mag: u32, | ||||
|     }, | ||||
|     Shop, | ||||
| } | ||||
| 
 | ||||
| impl From<ItemLocation> for PgItemLocationDetail { | ||||
|     fn from(other: ItemLocation) -> PgItemLocationDetail { | ||||
|         match other { | ||||
|             ItemLocation::Inventory{character_id, slot, equipped} => PgItemLocationDetail::Inventory{character_id: character_id.0, slot, equipped}, | ||||
|             ItemLocation::Bank{character_id, name} => PgItemLocationDetail::Bank{character_id: character_id.0, name: name.0}, | ||||
|             ItemLocation::LocalFloor{character_id, map_area, x,y,z} => PgItemLocationDetail::LocalFloor{character_id: character_id.0, map_area, x,y,z}, | ||||
|             ItemLocation::SharedFloor{map_area, x,y,z} => PgItemLocationDetail::SharedFloor{map_area, x,y,z}, | ||||
|             ItemLocation::Consumed => PgItemLocationDetail::Consumed, | ||||
|             ItemLocation::FedToMag{mag} => PgItemLocationDetail::FedToMag{mag: mag.0}, | ||||
|             ItemLocation::Shop => PgItemLocationDetail::Shop, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Into<ItemLocation> for PgItemLocationDetail { | ||||
|     fn into(self) -> ItemLocation { | ||||
|         match self { | ||||
|             PgItemLocationDetail::Inventory{character_id, slot, equipped} => ItemLocation::Inventory{character_id: CharacterEntityId(character_id), slot, equipped}, | ||||
|             PgItemLocationDetail::Bank{character_id, name} => ItemLocation::Bank{character_id: CharacterEntityId(character_id), name: BankName(name)}, | ||||
|             PgItemLocationDetail::LocalFloor{character_id, map_area, x,y,z} => ItemLocation::LocalFloor{character_id: CharacterEntityId(character_id), map_area, x,y,z}, | ||||
|             PgItemLocationDetail::SharedFloor{map_area, x,y,z} => ItemLocation::SharedFloor{map_area, x,y,z}, | ||||
|             PgItemLocationDetail::Consumed => ItemLocation::Consumed, | ||||
|             PgItemLocationDetail::FedToMag{mag} => ItemLocation::FedToMag{mag: ItemEntityId(mag)}, | ||||
|             PgItemLocationDetail::Shop => ItemLocation::Shop, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Debug, sqlx::FromRow)] | ||||
| pub struct PgItemLocation { | ||||
|     //pub id: i32,
 | ||||
|     pub location: sqlx::types::Json<PgItemLocationDetail>, | ||||
|     created_at: chrono::DateTime<chrono::Utc>, | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub enum PgMagModifierDetail { | ||||
|     FeedMag(i32), | ||||
|     BankMag, | ||||
|     MagCell(i32), | ||||
|     OwnerChange(CharacterClass, SectionID) | ||||
| } | ||||
| 
 | ||||
| impl From<mag::MagModifier> for PgMagModifierDetail { | ||||
|     fn from(other: mag::MagModifier) -> PgMagModifierDetail { | ||||
|         match other { | ||||
|             mag::MagModifier::FeedMag{food} => PgMagModifierDetail::FeedMag(food.0 as i32), | ||||
|             mag::MagModifier::BankMag => PgMagModifierDetail::BankMag, | ||||
|             mag::MagModifier::MagCell(cell) => PgMagModifierDetail::MagCell(cell.0 as i32), | ||||
|             mag::MagModifier::OwnerChange(class, section_id) => PgMagModifierDetail::OwnerChange(class, section_id), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Into<mag::MagModifier> for PgMagModifierDetail { | ||||
|     fn into(self) -> mag::MagModifier { | ||||
|         match self { | ||||
|             PgMagModifierDetail::FeedMag(food) => mag::MagModifier::FeedMag{food: ItemEntityId(food as u32)}, | ||||
|             PgMagModifierDetail::BankMag => mag::MagModifier::BankMag, | ||||
|             PgMagModifierDetail::MagCell(cell) => mag::MagModifier::MagCell(ItemEntityId(cell as u32)), | ||||
|             PgMagModifierDetail::OwnerChange(class, section_id) => mag::MagModifier::OwnerChange(class, section_id), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, sqlx::FromRow)] | ||||
| pub struct PgMagModifier { | ||||
|     mag: i32, | ||||
|     pub modifier: sqlx::types::Json<PgMagModifierDetail>, | ||||
|     created_at: chrono::DateTime<chrono::Utc>, | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Debug, sqlx::FromRow)] | ||||
| pub struct PgItemWithLocation { | ||||
|     pub id: i32, | ||||
|     pub item: sqlx::types::Json<PgItemDetail>, | ||||
|     pub location: sqlx::types::Json<PgItemLocationDetail>, | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Debug, sqlx::FromRow)] | ||||
| pub struct PgMagModifierWithParameters { | ||||
|     pub mag: i32, | ||||
|     pub modifier: sqlx::types::Json<PgMagModifierDetail>, | ||||
|     pub feed: Option<sqlx::types::Json<PgTool>>, | ||||
|     pub cell: Option<sqlx::types::Json<PgTool>>, | ||||
| } | ||||
							
								
								
									
										343
									
								
								src/entity/gateway/postgres/postgres.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										343
									
								
								src/entity/gateway/postgres/postgres.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,343 @@ | ||||
| use std::convert::{From, TryFrom, Into, TryInto}; | ||||
| use serde::{Serialize, Deserialize}; | ||||
| use futures::future::join_all; | ||||
| use futures::TryStreamExt; | ||||
| //use futures::StreamExt;
 | ||||
| use async_std::stream::StreamExt; | ||||
| //use futures::StreamExt;
 | ||||
| use libpso::character::{settings, guildcard}; | ||||
| use libpso::util::vec_to_array; | ||||
| use crate::entity::account::*; | ||||
| use crate::entity::character::*; | ||||
| use crate::entity::gateway::EntityGateway; | ||||
| use crate::entity::item::*; | ||||
| use super::models::*; | ||||
| 
 | ||||
| use sqlx::postgres::PgPoolOptions; | ||||
| use sqlx::Row; | ||||
| use sqlx::Execute; | ||||
| use postgres::{Client, NoTls}; | ||||
| 
 | ||||
| mod embedded { | ||||
|     use refinery::embed_migrations; | ||||
|     embed_migrations!("src/entity/gateway/postgres/migrations"); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct PostgresGateway { | ||||
|     pool: sqlx::Pool<sqlx::Postgres>, | ||||
| } | ||||
| 
 | ||||
| impl PostgresGateway { | ||||
|     pub fn new(host: &str, dbname: &str, username: &str, password: &str) -> PostgresGateway { | ||||
|         // the postgres dep can be removed once refinery supports sqlx
 | ||||
|         let mut conn = Client::connect(&format!("host='{}' dbname='{}' user='{}' password='{}'", host, dbname, username, password), NoTls).unwrap(); | ||||
|         embedded::migrations::runner().run(&mut conn).unwrap(); | ||||
|         
 | ||||
|         let pool = async_std::task::block_on(async move { | ||||
|             let pool = PgPoolOptions::new() | ||||
|                 .max_connections(5) | ||||
|                 .connect(&format!("postgresql://{}:{}@{}:5432/{}", username, password, host, dbname)).await.unwrap(); | ||||
| 
 | ||||
|             pool | ||||
|         }); | ||||
|         
 | ||||
|         PostgresGateway { | ||||
|             pool: pool, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async fn apply_item_modifications(&self, item: ItemEntity) -> ItemEntity { | ||||
|         let ItemEntity {id, item, location} = item; | ||||
|         
 | ||||
|         let item = match item { | ||||
|             ItemDetail::Mag(mut mag) => { | ||||
|                 let q = r#"select mag, modifier, item.item -> 'Tool' as feed, item2.item -> 'Tool' as cell 
 | ||||
|                            from mag_modifier 
 | ||||
|                            left join item on item.id = cast (modifier ->> 'FeedMag' as integer) | ||||
|                            left join item as item2 on item2.id = cast (modifier ->> 'MagCell' as integer) | ||||
|                            where mag = $1 order by created_at"#;
 | ||||
|                 let mag_modifiers = sqlx::query_as::<_, PgMagModifierWithParameters>(q) | ||||
|                     .bind(id.0 as i32) | ||||
|                     .fetch(&self.pool); | ||||
| 
 | ||||
|                 mag_modifiers.for_each(|modifier| { | ||||
|                     let PgMagModifierWithParameters {modifier, feed, cell, ..} = modifier.unwrap(); | ||||
|                     let modifier: mag::MagModifier = modifier.0.into(); | ||||
|                     match modifier { | ||||
|                         mag::MagModifier::FeedMag{..} => { | ||||
|                             mag.feed(feed.unwrap().tool) | ||||
|                         }, | ||||
|                         mag::MagModifier::BankMag => { | ||||
|                             mag.bank() | ||||
|                         }, | ||||
|                         mag::MagModifier::MagCell(_) => { | ||||
|                             mag.apply_mag_cell(mag::MagCell::try_from(Into::<tool::Tool>::into(cell.unwrap().0).tool).unwrap()) | ||||
|                         }, | ||||
|                         mag::MagModifier::OwnerChange(class, section_id) => { | ||||
|                             mag.change_owner(class, section_id) | ||||
|                         }, | ||||
|                     } | ||||
|                 }).await; | ||||
|                 
 | ||||
|                 ItemDetail::Mag(mag) | ||||
|             }, | ||||
|             item @ _ => item | ||||
|         }; | ||||
| 
 | ||||
|         ItemEntity { | ||||
|             id: id, | ||||
|             item: item, | ||||
|             location: location | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[async_trait::async_trait] | ||||
| impl EntityGateway for PostgresGateway { | ||||
|     async fn create_user(&mut self, user: NewUserAccountEntity) -> Option<UserAccountEntity> { | ||||
|         let new_user = sqlx::query_as::<_, PgUserAccount>("insert into user_accounts (name, password) values ($1, $2) returning *;") | ||||
|             .bind(user.username) | ||||
|             .bind(user.password) | ||||
|             .fetch_one(&self.pool).await.unwrap(); | ||||
|         Some(new_user.into()) | ||||
|     } | ||||
| 
 | ||||
|     async fn get_user_by_id(&self, id: UserAccountId) -> Option<UserAccountEntity> { | ||||
|         let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where id = $1") | ||||
|             .bind(id.0) | ||||
|             .fetch_one(&self.pool).await.unwrap(); | ||||
|         Some(user.into()) | ||||
|     } | ||||
| 
 | ||||
|     async fn get_user_by_name(&self, username: String) -> Option<UserAccountEntity> { | ||||
|         let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where name = $1") | ||||
|             .bind(username) | ||||
|             .fetch_one(&self.pool).await.unwrap(); | ||||
|         Some(user.into()) | ||||
|     } | ||||
| 
 | ||||
|     async fn save_user(&mut self, user: &UserAccountEntity) { | ||||
|         sqlx::query("UPDATE user_accounts set name=$1, password=$2, banned=$3, muted=$4, flags=$5 where id=$6") | ||||
|             .bind(&user.username) | ||||
|             .bind(&user.password) | ||||
|             .bind(&user.banned_until) | ||||
|             .bind(&user.muted_until) | ||||
|             .bind(&user.flags) | ||||
|             .bind(&user.id.0) | ||||
|             .fetch_one(&self.pool).await.unwrap(); | ||||
|     } | ||||
| 
 | ||||
|     async fn create_user_settings(&mut self, settings: NewUserSettingsEntity) -> Option<UserSettingsEntity> { | ||||
|         let new_settings = sqlx::query_as::<_, PgUserSettings>("insert into user_settings (user_account, blocked_users, key_config, joystick_config, option_flags, shortcuts, symbol_chats, team_name)
 | ||||
|                                                                 values ($1, $2, $3, $4, $5, $6, $7, $8) returning *;")
 | ||||
|             .bind(settings.user_id.0) | ||||
|             .bind(settings.settings.blocked_users.to_vec().into_iter().map(|i| i.to_le_bytes().to_vec()).flatten().collect::<Vec<u8>>()) | ||||
|             .bind(settings.settings.key_config.to_vec()) | ||||
|             .bind(settings.settings.joystick_config.to_vec()) | ||||
|             .bind(settings.settings.option_flags as i32) | ||||
|             .bind(settings.settings.shortcuts.to_vec()) | ||||
|             .bind(settings.settings.symbol_chats.to_vec()) | ||||
|             .bind(settings.settings.team_name.to_vec().into_iter().map(|i| i.to_le_bytes().to_vec()).flatten().collect::<Vec<u8>>()) | ||||
|             .fetch_one(&self.pool).await.unwrap(); | ||||
|         Some(new_settings.into()) | ||||
|     } | ||||
| 
 | ||||
|     async fn get_user_settings_by_user(&self, user: &UserAccountEntity) -> Option<UserSettingsEntity> { | ||||
|         let settings = sqlx::query_as::<_, PgUserSettings>("select * from user_settings where id = $1") | ||||
|             .bind(user.id.0) | ||||
|             .fetch_one(&self.pool).await.unwrap(); | ||||
|         Some(settings.into()) | ||||
|     } | ||||
| 
 | ||||
|     async fn save_user_settings(&mut self, settings: &UserSettingsEntity) { | ||||
|         sqlx::query("update user_settings set blocked_users=$1, key_config=$2, joystick_config=$3, option_flags=$4, shortcuts=$5, symbol_chats=$6, team_name=$7 where id=$8") | ||||
|             .bind(settings.settings.blocked_users.to_vec().into_iter().map(|i| i.to_le_bytes().to_vec()).flatten().collect::<Vec<u8>>()) | ||||
|             .bind(&settings.settings.key_config.to_vec()) | ||||
|             .bind(&settings.settings.joystick_config.to_vec()) | ||||
|             .bind(&settings.settings.option_flags) | ||||
|             .bind(&settings.settings.shortcuts.to_vec()) | ||||
|             .bind(&settings.settings.symbol_chats.to_vec()) | ||||
|             .bind(settings.settings.team_name.to_vec().into_iter().map(|i| i.to_le_bytes().to_vec()).flatten().collect::<Vec<u8>>()) | ||||
|             .bind(&settings.id.0) | ||||
|             .fetch_one(&self.pool).await.unwrap(); | ||||
|     } | ||||
| 
 | ||||
|     async fn create_character(&mut self, char: NewCharacterEntity) -> Option<CharacterEntity> { | ||||
|         let q = r#"insert into player_character
 | ||||
|                   (user_account, slot, name, exp, class, section_id, costume, skin, face, head, hair, hair_r, hair_g, hair_b, prop_x, prop_y, techs, 
 | ||||
|                    config, infoboard, guildcard, power, mind, def, evade, luck, hp, tp, tech_menu, meseta, bank_meseta) | ||||
|                   values | ||||
|                   ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30) | ||||
|                   returning *;"#;
 | ||||
|         let character = sqlx::query_as::<_, PgCharacter>(q) | ||||
|         //sqlx::query(q)
 | ||||
|             .bind(char.user_id.0) | ||||
|             .bind(char.slot as i16) | ||||
|             .bind(char.name) | ||||
|             .bind(char.exp as i32) | ||||
|             .bind(char.char_class.to_string()) | ||||
|             .bind(char.section_id.to_string()) | ||||
|             .bind(char.appearance.costume as i16) | ||||
|             .bind(char.appearance.skin as i16) | ||||
|             .bind(char.appearance.face as i16) | ||||
|             .bind(char.appearance.head as i16) | ||||
|             .bind(char.appearance.hair as i16) | ||||
|             .bind(char.appearance.hair_r as i16) | ||||
|             .bind(char.appearance.hair_g as i16) | ||||
|             .bind(char.appearance.hair_b as i16) | ||||
|             .bind(char.appearance.prop_x) | ||||
|             .bind(char.appearance.prop_y) | ||||
|             .bind(&char.techs.as_bytes().to_vec()) | ||||
|             .bind(&char.config.as_bytes().to_vec()) | ||||
|             .bind(String::from_utf16_lossy(&char.info_board.board).trim_matches(char::from(0))) | ||||
|             .bind(char.guildcard.description) | ||||
|             .bind(char.materials.power as i16) | ||||
|             .bind(char.materials.mind as i16) | ||||
|             .bind(char.materials.def as i16) | ||||
|             .bind(char.materials.evade as i16) | ||||
|             .bind(char.materials.luck as i16) | ||||
|             .bind(char.materials.hp as i16) | ||||
|             .bind(char.materials.tp as i16) | ||||
|             .bind(char.tech_menu.tech_menu.to_vec()) | ||||
|             .bind(char.meseta as i32) | ||||
|             .bind(char.bank_meseta as i32) | ||||
|             .fetch_one(&self.pool).await.unwrap(); | ||||
|         Some(character.into()) | ||||
|     } | ||||
| 
 | ||||
|     async fn get_characters_by_user(&self, user: &UserAccountEntity) -> [Option<CharacterEntity>; 4] { | ||||
|         let mut stream = sqlx::query_as::<_, PgCharacter>("select * from player_character where user_account = $1 and slot < 4 order by slot") | ||||
|             .bind(user.id.0) | ||||
|             .fetch(&self.pool); | ||||
|         let mut result = [None; 4]; | ||||
|         while let Some(character) = stream.try_next().await.unwrap() { | ||||
|             let index = character.slot as usize; | ||||
|             result[index] = Some(character.into()) | ||||
|         } | ||||
|         
 | ||||
|         result | ||||
|     } | ||||
| 
 | ||||
|     async fn save_character(&mut self, char: &CharacterEntity) { | ||||
|         let q = r#"update player_character set
 | ||||
|                    user_account=$1, slot=$2, name=$3, exp=$4, class=$5, section_id=$6, costume=$7, skin=$8, face=$9, head=$10, hair=$11, hair_r=$12, | ||||
|                    hair_g=$13, hair_b=$14, prop_x=$15, prop_y=$16, techs=$17, config=$18, infoboard=$19, guildcard=$20, power=$21, mind=$22, def=$23, | ||||
|                    evade=$24, luck=$25, hp=$26, tp=$27, tech_menu=$28, meseta=$29, bank_meseta=$30 | ||||
|                    where id=$31;"#;
 | ||||
|         sqlx::query(q) | ||||
|             .bind(char.user_id.0) | ||||
|             .bind(char.slot as i16) | ||||
|             .bind(&char.name) | ||||
|             .bind(char.exp as i32) | ||||
|             .bind(char.char_class.to_string()) | ||||
|             .bind(char.section_id.to_string()) | ||||
|             .bind(char.appearance.costume as i16) | ||||
|             .bind(char.appearance.skin as i16) | ||||
|             .bind(char.appearance.face as i16) | ||||
|             .bind(char.appearance.head as i16) | ||||
|             .bind(char.appearance.hair as i16) | ||||
|             .bind(char.appearance.hair_r as i16) | ||||
|             .bind(char.appearance.hair_g as i16) | ||||
|             .bind(char.appearance.hair_b as i16) | ||||
|             .bind(char.appearance.prop_x) | ||||
|             .bind(char.appearance.prop_y) | ||||
|             .bind(&char.techs.as_bytes().to_vec()) | ||||
|             .bind(&char.config.as_bytes().to_vec()) | ||||
|             .bind(String::from_utf16_lossy(&char.info_board.board).trim_matches(char::from(0))) | ||||
|             .bind(&char.guildcard.description) | ||||
|             .bind(char.materials.power as i16) | ||||
|             .bind(char.materials.mind as i16) | ||||
|             .bind(char.materials.def as i16) | ||||
|             .bind(char.materials.evade as i16) | ||||
|             .bind(char.materials.luck as i16) | ||||
|             .bind(char.materials.hp as i16) | ||||
|             .bind(char.materials.tp as i16) | ||||
|             .bind(char.tech_menu.tech_menu.to_vec()) | ||||
|             .bind(char.meseta as i32) | ||||
|             .bind(char.bank_meseta as i32) | ||||
|             .bind(char.id.0 as i32) | ||||
|             .execute(&self.pool).await.unwrap(); | ||||
|     } | ||||
| 
 | ||||
|     async fn get_guild_card_data_by_user(&self, user: &UserAccountEntity) -> GuildCardDataEntity { | ||||
|         GuildCardDataEntity { | ||||
|             id: GuildCardDataId(0), | ||||
|             user_id: user.id, | ||||
|             guildcard: guildcard::GuildCardData::default(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async fn create_item(&mut self, item: NewItemEntity) -> Option<ItemEntity> { | ||||
|         let new_item = sqlx::query_as::<_, PgItem>("insert into item (item) values ($1) returning *;") | ||||
|             .bind(sqlx::types::Json(PgItemDetail::from(item.item))) | ||||
|             .fetch_one(&self.pool).await.unwrap(); | ||||
|         let location = sqlx::query_as::<_, PgItemLocation>("insert into item_location (item, location) values ($1, $2) returning *;") | ||||
|             .bind(new_item.id) | ||||
|             .bind(sqlx::types::Json(PgItemLocationDetail::from(item.location))) | ||||
|             .fetch_one(&self.pool).await.unwrap(); | ||||
|         Some(ItemEntity { | ||||
|             id: ItemEntityId(new_item.id as u32), | ||||
|             item: new_item.item.0.into(), | ||||
|             location: location.location.0.into(), | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) { | ||||
|         sqlx::query("insert into item_location (item, location) values ($1, $2);") | ||||
|             .bind(item_id.0) | ||||
|             .bind(sqlx::types::Json(PgItemLocationDetail::from(item_location))) | ||||
|             .fetch_one(&self.pool).await.unwrap(); | ||||
|     } | ||||
| 
 | ||||
|     async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) { | ||||
|         sqlx::query("insert into item_location (item, location) values ($1, $2);") | ||||
|             .bind(tool_item_id.0) | ||||
|             .bind(sqlx::types::Json(PgItemLocationDetail::from(ItemLocation::FedToMag {mag: *mag_item_id}))) | ||||
|             .execute(&self.pool).await.unwrap(); | ||||
|         sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") | ||||
|             .bind(mag_item_id.0) | ||||
|             .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::FeedMag{food: *tool_item_id}))) | ||||
|             .execute(&self.pool).await.unwrap(); | ||||
|     } | ||||
| 
 | ||||
|     async fn change_mag_owner(&mut self, mag_item_id: &ItemEntityId, character: &CharacterEntity) { | ||||
|         sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") | ||||
|             .bind(mag_item_id.0) | ||||
|             .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::OwnerChange(character.char_class, character.section_id)))) | ||||
|             .execute(&self.pool).await.unwrap(); | ||||
|     } | ||||
| 
 | ||||
|     async fn use_mag_cell(&mut self, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) { | ||||
|         // consume cell?
 | ||||
|         sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") | ||||
|             .bind(mag_item_id.0) | ||||
|             .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::MagCell(*mag_cell_id)))) | ||||
|             .execute(&self.pool).await.unwrap(); | ||||
|     } | ||||
| 
 | ||||
|     async fn get_items_by_character(&self, char: &CharacterEntity) -> Vec<ItemEntity> { | ||||
|         let q = r#"select item.id, item_location.location, item.item from item_location
 | ||||
|                    join item on item.id = item_location.item | ||||
|                    where cast (location -> 'Inventory' ->> 'character_id' as integer) = $1 | ||||
|                       or cast (location -> 'Bank' ->> 'character_id' as integer) = $1"#;
 | ||||
|         let items = sqlx::query_as::<_, PgItemWithLocation>(q) | ||||
|             .bind(char.id.0) | ||||
|             .fetch(&self.pool); | ||||
|         join_all(items | ||||
|             .filter_map(|item: Result<PgItemWithLocation, _>| { | ||||
|                 let item = item.ok()?; | ||||
|                 Some(ItemEntity { | ||||
|                     id: ItemEntityId(item.id as u32), | ||||
|                     item: item.item.0.into(), | ||||
|                     location: item.location.0.into() | ||||
|                 }) | ||||
|             }) | ||||
|             .map(|item: ItemEntity| { | ||||
|                 self.apply_item_modifications(item) | ||||
|             }) | ||||
|             .collect::<Vec<_>>() | ||||
|             .await).await | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user