This commit is contained in:
ntr 2019-02-21 12:05:27 +11:00
parent 94eb259409
commit 613444d066
8 changed files with 311 additions and 129 deletions

View File

@ -1,26 +1,40 @@
const NULL_UUID = '00000000-0000-0000-0000-000000000000';
exports.up = async knex => {
await knex.schema.createTable('games', table => {
table.uuid('id').primary();
table.index('id');
table.timestamps();
table.binary('data').notNullable();
});
await knex.schema.createTable('instances', table => {
table.uuid('id').primary();
table.index('id');
table.timestamps();
table.binary('data').notNullable();
table.boolean('joinable')
table.boolean('open')
.defaultTo(true)
.notNullable();
await knex.schema.raw(
// eslint-disable-next-line max-len
'CREATE UNIQUE INDEX instances_joinable ON instances WHERE open = true;'
);
});
await knex.schema.createTable('players', table => {
table.uuid('id').primary();
table.index('id');
// the game itself
table.uuid('game').notNullable()
table.foreign('game')
// the instance
table.uuid('instance').notNullable()
table.foreign('instance')
.references('id')
.inTable('games')
.inTable('instances')
.onDelete('CASCADE');
table.index('game');
table.index('instance');
// account in a game
table.uuid('account').notNullable()
@ -30,6 +44,15 @@ exports.up = async knex => {
.onDelete('CASCADE');
table.index('account');
table.unique(['account', 'instance']);
});
// not really sure if this is a good idea
await knex('instances').insert({
id: NULL_UUID,
data: 'INVALID',
joinable: false,
});
};

View File

@ -1,36 +0,0 @@
const NULL_UUID = '00000000-0000-0000-0000-000000000000';
exports.up = async knex => {
await knex.schema.createTable('vbox', table => {
table.timestamps();
table.uuid('id').primary();
table.uuid('account').notNullable()
table.uuid('game').notNullable()
table.binary('data').notNullable();
table.foreign('game')
.references('id')
.inTable('games')
.onDelete('CASCADE');
table.foreign('account')
.references('id')
.inTable('accounts')
.onDelete('CASCADE');
table.index('id');
table.index('account');
table.unique(['account', 'game']);
});
// not really sure if this is a good idea
await knex('games').insert({
id: NULL_UUID,
data: 'INVALID',
joinable: false,
});
return true;
};
exports.down = async () => {};

View File

@ -1,30 +1,3 @@
# Principles
* Experience something
* Express something
* Prove something
* 1: Fighting against human nature is a losing game
* 2: Aesthetics matter
* 3: Resonance is important
* 4: Make use of piggybacking
* 5: Don't confuse "interesting" with "fun"
* 6: Understand what emotion your game is trying to evoke
* 7: Allow the players the skill to make the game personal
* 8: The details are where the players fall in love with your game
* 9: Allow your players to have a sense of ownership
* 10: Leave room for the player to explore
* 11: If everyone likes your game, but no one loves it, it will fail
* 12: Don't design to prove you can do something
* 13: Make the fun part also the correct strategy to win
* 14: Don't be afraid to be blunt
* 15: Design the component for its intended audience
* 16: Be more afraid of boring your players than challenging them
* 17: You don't have to change much to change everything
* 18: Restrictions breed creativity
* 19: Your audience is good at recognizing problems and bad at solving them
* 20: All the lessons connect
# Key Mechanics
* 10d chaos maths, not rock paper scissors
* phys is faster and chaotic
@ -33,28 +6,13 @@
* red_shield is restored, not gained
* players can feel aggressive
# ask sam
* main screen layout
* ping icon
*
* combat screen layout
* health bars
* statuses
* icons
* skill type / damage type
* skills themselves
* passive nodes / notables
* tell him about discord
# WORK WORK
broken skills
strangle
taunt
## NOW
cost system for items
design / implement specs
combo specs
round system for games
## SOON
* clean up categories
@ -119,3 +77,29 @@ gem td style attr combinations
slimey
ghostly
# Principles
* Experience something
* Express something
* Prove something
* 1: Fighting against human nature is a losing game
* 2: Aesthetics matter
* 3: Resonance is important
* 4: Make use of piggybacking
* 5: Don't confuse "interesting" with "fun"
* 6: Understand what emotion your game is trying to evoke
* 7: Allow the players the skill to make the game personal
* 8: The details are where the players fall in love with your game
* 9: Allow your players to have a sense of ownership
* 10: Leave room for the player to explore
* 11: If everyone likes your game, but no one loves it, it will fail
* 12: Don't design to prove you can do something
* 13: Make the fun part also the correct strategy to win
* 14: Don't be afraid to be blunt
* 15: Design the component for its intended audience
* 16: Be more afraid of boring your players than challenging them
* 17: You don't have to change much to change everything
* 18: Restrictions breed creativity
* 19: Your audience is good at recognizing problems and bad at solving them
* 20: All the lessons connect

View File

@ -648,26 +648,6 @@ pub fn game_get(tx: &mut Transaction, id: Uuid) -> Result<Game, Error> {
return Ok(game);
}
pub fn players_write(account: &Account, game_id: Uuid, tx: &mut Transaction) -> Result<(), Error> {
// pve
let id = Uuid::new_v4();
let query = "
INSERT INTO players (id, game, account)
VALUES ($1, $2, $3)
RETURNING id, account;
";
let result = tx
.query(query, &[&id, &game_id, &account.id])?;
let _returned = result.iter().next().expect("no row written");
println!("wrote player {:} joined game: {:}", account.name, game_id);
return Ok(());
}
pub fn game_update(game: &Game, tx: &mut Transaction) -> Result<(), Error> {
let game_bytes = to_vec(&game)?;
@ -769,7 +749,6 @@ pub fn game_pvp(params: GamePvpParams, tx: &mut Transaction, account: &Account)
// persist
game_write(&game, tx)?;
players_write(account, game_id, tx)?;
Ok(game)
}
@ -803,7 +782,6 @@ pub fn game_join(params: GameJoinParams, tx: &mut Transaction, account: &Account
}
game_update(&game, tx)?;
players_write(account, game_id, tx)?;
Ok(game)
}

137
server/src/instance.rs Normal file
View File

@ -0,0 +1,137 @@
use uuid::Uuid;
use serde_cbor::{from_slice, to_vec};
use postgres::transaction::Transaction;
use failure::Error;
use failure::err_msg;
use account::Account;
use player::{Player, player_write};
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
enum InstancePhase {
Open,
Vbox,
Combat,
Finished,
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct Instance {
id: Uuid,
players: Vec<Uuid>,
phase: InstancePhase,
open: bool,
}
impl Instance {
fn new() -> Instance {
Instance {
id: Uuid::new_v4(),
players: vec![],
phase: InstancePhase::Open,
open: true,
}
}
fn add_player(&mut self, player: Player) -> &mut Instance {
self.players.push(player.id);
self
}
}
pub fn instance_create(instance: Instance, tx: &mut Transaction) -> Result<Instance, Error> {
let instance_bytes = to_vec(&instance)?;
let query = "
INSERT INTO instances (id, data)
VALUES ($1, $2)
RETURNING id;
";
let result = tx
.query(query, &[&instance.id, &instance_bytes])?;
result.iter().next().ok_or(format_err!("no instances written"))?;
return Ok(instance);
}
pub fn instance_write(instance: Instance, tx: &mut Transaction) -> Result<Instance, Error> {
let instance_bytes = to_vec(&instance)?;
let query = "
UPDATE instance
SET data = $1
WHERE id = $2
RETURNING id, data;
";
let result = tx
.query(query, &[&instance_bytes, &instance.id])?;
result.iter().next().ok_or(err_msg("no instance row returned"))?;
// println!("{:?} wrote instance", instance.id);
return Ok(instance);
}
pub fn instance_get(tx: &mut Transaction, instance_id: Uuid) -> Result<Instance, Error> {
let query = "
SELECT *
FROM instance
WHERE id = $1;
";
let result = tx
.query(query, &[&instance_id])?;
let returned = match result.iter().next() {
Some(row) => row,
None => return Err(err_msg("instance not found")),
};
let instance_bytes: Vec<u8> = returned.get("data");
let instance = from_slice::<Instance>(&instance_bytes)?;
return Ok(instance);
}
pub fn instance_get_open(tx: &mut Transaction) -> Result<Instance, Error> {
let query = "
SELECT *
FROM instance
AND open = true;
";
let result = tx
.query(query, &[])?;
let returned = match result.iter().next() {
Some(row) => row,
None => return Err(err_msg("instance not found")),
};
let instance_bytes: Vec<u8> = returned.get("data");
let instance = from_slice::<Instance>(&instance_bytes)?;
return Ok(instance);
}
pub fn join_instance(tx: &mut Transaction, account: &Account) -> Result<Player, Error> {
let mut instance = match instance_get_open(tx) {
Ok(i) => i,
Err(_) => instance_create(Instance::new())?,
};
// get add them
let player = Player::new(account, instance.id);
instance.add_player(account);
instance_write(instance, tx)?;
return player_write(player, tx);
}

View File

@ -26,7 +26,8 @@ mod spec;
// mod passives;
mod rpc;
mod account;
// mod item;
mod instance;
mod player;
mod zone;
mod mob;

82
server/src/player.rs Normal file
View File

@ -0,0 +1,82 @@
use uuid::Uuid;
use serde_cbor::{from_slice, to_vec};
use postgres::transaction::Transaction;
use failure::Error;
use failure::err_msg;
use account::Account;
use rpc::{VboxStateParams, VboxAcceptParams, VboxDiscardParams, VboxCombineParams, VboxApplyParams, VboxDropParams};
use cryp::{Cryp, cryp_get, cryp_write};
use game::{Team, GameMode};
use vbox::{Vbox};
pub struct Score {
wins: u8,
losses: u8,
}
pub struct Player {
id: Uuid,
// name: String,
vbox: Vbox,
score: Score,
cryps: Vec<Cryp>,
}
impl Player {
pub fn new(account: Uuid, instance: Uuid, cryps: Vec<Cryp>) -> Player {
Player {
id: account,
vbox: Vbox::new(account, instance),
score: Score { wins: 0, losses: 0 },
cryps,
}
}
}
pub fn player_get(tx: &mut Transaction, instance_id: Uuid) -> Result<Instance, Error> {
let query = "
SELECT *
FROM players
WHERE account = $1
AND instance = $2;
";
let result = tx
.query(query, &[&instance_id])?;
let returned = match result.iter().next() {
Some(row) => row,
None => return Err(err_msg("instance not found")),
};
// tells from_slice to cast into a cryp
let instance_bytes: Vec<u8> = returned.get("data");
let instance = from_slice::<Instance>(&instance_bytes)?;
return Ok(instance);
}
pub fn player_create(player: Player, account: &Account, instance_id: Uuid, tx: &mut Transaction) -> Result<(), Error> {
let player_bytes = to_vec(&player)?;
let id = Uuid::new_v4();
let query = "
INSERT INTO players (id, instance, account, data)
VALUES ($1, $2, $3, $4)
RETURNING id, account;
";
let result = tx
.query(query, &[&id, &instance_id, &account.id, player_bytes])?;
let _returned = result.iter().next().expect("no row written");
println!("wrote player {:} joined game: {:}", account.name, instance_id);
return Ok(());
}

View File

@ -62,14 +62,14 @@ impl Rpc {
// auth methods
"cryp_spawn" => Rpc::cryp_spawn(data, &mut tx, account.unwrap(), client),
"cryp_learn" => Rpc::cryp_learn(data, &mut tx, account.unwrap(), client),
"cryp_forget" => Rpc::cryp_forget(data, &mut tx, account.unwrap(), client),
"cryp_unspec" => Rpc::cryp_unspec(data, &mut tx, account.unwrap(), client),
// "cryp_learn" => Rpc::cryp_learn(data, &mut tx, account.unwrap(), client),
// "cryp_forget" => Rpc::cryp_forget(data, &mut tx, account.unwrap(), client),
// "cryp_unspec" => Rpc::cryp_unspec(data, &mut tx, account.unwrap(), client),
"game_state" => Rpc::game_state(data, &mut tx, account.unwrap(), client),
"game_pve" => Rpc::game_pve(data, &mut tx, account.unwrap(), client),
"game_pvp" => Rpc::game_pvp(data, &mut tx, account.unwrap(), client),
"game_join" => Rpc::game_join(data, &mut tx, account.unwrap(), client),
"game_joinable_list" => Rpc::game_joinable_list(data, &mut tx, account.unwrap(), client),
// "game_join" => Rpc::game_join(data, &mut tx, account.unwrap(), client),
// "game_joinable_list" => Rpc::game_joinable_list(data, &mut tx, account.unwrap(), client),
"game_skill" => Rpc::game_skill(data, &mut tx, account.unwrap(), client),
// "game_target" => Rpc::game_target(data, &mut tx, account.unwrap(), client),
"zone_create" => Rpc::zone_create(data, &mut tx, account.unwrap(), client),
@ -78,12 +78,13 @@ impl Rpc {
"account_cryps" => Rpc::account_cryps(data, &mut tx, account.unwrap(), client),
"account_zone" => Rpc::account_zone(data, &mut tx, account.unwrap(), client),
"vbox_state" => Rpc::vbox_state(data, &mut tx, account.unwrap(), client),
"vbox_accept" => Rpc::vbox_accept(data, &mut tx, account.unwrap(), client),
"vbox_apply" => Rpc::vbox_apply(data, &mut tx, account.unwrap(), client),
"vbox_drop" => Rpc::vbox_drop(data, &mut tx, account.unwrap(), client),
"vbox_combine" => Rpc::vbox_combine(data, &mut tx, account.unwrap(), client),
"vbox_discard" => Rpc::vbox_discard(data, &mut tx, account.unwrap(), client),
"instance_join" => Rpc::instance_join(data, &mut tx, account.unwrap(), client),
"player_state" => Rpc::vbox_state(data, &mut tx, account.unwrap(), client),
"player_vbox_accept" => Rpc::player_vbox_accept(data, &mut tx, account.unwrap(), client),
"player_vbox_apply" => Rpc::player_vbox_apply(data, &mut tx, account.unwrap(), client),
"player_vbox_drop" => Rpc::player_vbox_drop(data, &mut tx, account.unwrap(), client),
"player_vbox_combine" => Rpc::player_vbox_combine(data, &mut tx, account.unwrap(), client),
"player_vbox_discard" => Rpc::player_vbox_discard(data, &mut tx, account.unwrap(), client),
_ => Err(format_err!("unknown method - {:?}", v.method)),
};
@ -426,6 +427,18 @@ impl Rpc {
return Ok(response);
}
fn vbox_drop(data: Vec<u8>, tx: &mut Transaction, account: Account, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
let msg = from_slice::<VboxDropMsg>(&data).or(Err(err_msg("invalid params")))?;
let response = RpcResponse {
method: "vbox_state".to_string(),
params: RpcResult::VboxState(vbox_drop(msg.params, tx, &account)?)
};
return Ok(response);
}
}
#[derive(Debug,Clone,Serialize,Deserialize)]