wip
This commit is contained in:
parent
94eb259409
commit
613444d066
@ -1,35 +1,58 @@
|
||||
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')
|
||||
.defaultTo(true)
|
||||
.notNullable();
|
||||
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')
|
||||
.references('id')
|
||||
.inTable('games')
|
||||
.onDelete('CASCADE');
|
||||
table.index('game');
|
||||
// the instance
|
||||
table.uuid('instance').notNullable()
|
||||
table.foreign('instance')
|
||||
.references('id')
|
||||
.inTable('instances')
|
||||
.onDelete('CASCADE');
|
||||
table.index('instance');
|
||||
|
||||
// account in a game
|
||||
table.uuid('account').notNullable()
|
||||
table.foreign('account')
|
||||
.references('id')
|
||||
.inTable('accounts')
|
||||
.onDelete('CASCADE');
|
||||
.references('id')
|
||||
.inTable('accounts')
|
||||
.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,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -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 () => {};
|
||||
@ -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
|
||||
|
||||
@ -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
137
server/src/instance.rs
Normal 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);
|
||||
}
|
||||
@ -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
82
server/src/player.rs
Normal 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(());
|
||||
}
|
||||
@ -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)]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user