Initial commit for deckard and company
This commit is contained in:
commit
030195bfb2
79
API/decard.pl
Normal file
79
API/decard.pl
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#!/usr/bin/perl -w
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use JSON;
|
||||||
|
# use database engine
|
||||||
|
|
||||||
|
sub get_request_info {
|
||||||
|
my $clientIP = ($ENV{"REMOTE_ADDR"} or "0.0.0.0");
|
||||||
|
my $proto = ($ENV{"REQUEST_SCHEME"} or "http");
|
||||||
|
my $get_query = (split(/\?/,($ENV{"QUERY_STRING"} or "")))[0];
|
||||||
|
my $post_query = "";
|
||||||
|
if ( $ENV{"CONTENT_LENGTH"} ) { read( <STDIN>, $post_query, $ENV{"CONTENT_LENGTH"}); }
|
||||||
|
my $referrer = ($ENV{"HTTP_REFERER"} or "");
|
||||||
|
return ($proto,$clientIP,$get_query,$post_query,$referrer);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub http_status {
|
||||||
|
my ($status,$content,$target) = @_;
|
||||||
|
my $header = "";
|
||||||
|
$header .= "status: ".$status."\r\n";
|
||||||
|
$header .= "Location: ".$target."\r\n" if $target;
|
||||||
|
$header .= "Content-Type: ".$content."\r\n" if $content;
|
||||||
|
$header .= "\r\n";
|
||||||
|
return $header;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub html_tag {
|
||||||
|
if ($_[1]) { return "<".($_[0] or "div").($_[2] or "").">".($_[1] or "")."</".($_[0] or "div").">\n";
|
||||||
|
} else {return "<".($_[0] or "div").($_[2] or "")." />\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub html_content {
|
||||||
|
return html_tag("html", html_tag( "head", html_tag( "title", shift ) . shift ) . html_tag( "body", shift ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub soft_die {
|
||||||
|
print http_status(500,"text/html; charset=utf-8");
|
||||||
|
print html_content("500","",shift);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub clean_input {
|
||||||
|
my $input = shift;
|
||||||
|
if ($input =~ m!%2F!) { soft_die( "Location/hax\r\n\r\n"; }
|
||||||
|
$input =~ s!%(..)!chr hex $1!ge;
|
||||||
|
$input =~ s!\+! !g;
|
||||||
|
return $input;
|
||||||
|
}
|
||||||
|
|
||||||
|
my @request = get_request_info();
|
||||||
|
my @get_params = split( "!", clean_input($request[2]) );
|
||||||
|
# my @post_params
|
||||||
|
my $directive = shift(@get_params);
|
||||||
|
if ( "new" eq $directive ) {
|
||||||
|
# new session, new deck, new location
|
||||||
|
# if deck/location parent sessionID
|
||||||
|
# if location public/private
|
||||||
|
# returns id and key of new object
|
||||||
|
} elsif ( "shuffle" eq $directive ) {
|
||||||
|
# shuffle - deckID - full/current - deckKey
|
||||||
|
# sheffles all cards in deckID's locationID if deckKey matches
|
||||||
|
} elsif ( "move" eq $directive ) {
|
||||||
|
# move - cardID - current locationID - current locationKey - destination locationID
|
||||||
|
# changes ownership of cardID to destination locationID if cardID is owned by current locationID and the current locationKey matchs
|
||||||
|
} elsif ( "getLocations" eq $directive ) {
|
||||||
|
# getLocations - sessionID - sessionKey
|
||||||
|
# return all locationIDs associated with sessionID as long as sessionKey matches
|
||||||
|
} elsif ( "getCards" eq $directive ) {
|
||||||
|
# getCards - locationID - locationKey
|
||||||
|
# returns all cardIDs for locationID as long as its public or the locationKey matches
|
||||||
|
} elsif ( "getDecks" eq $directive ) {
|
||||||
|
# getDecks - sessionID - sessionKey
|
||||||
|
# returns all deckIDs associated with sessionID provided sessionKey matchs.
|
||||||
|
} else {
|
||||||
|
print http_status(200,"text/json");
|
||||||
|
print '{"error":"No input or incorrect input", "input":"'.$directive.'!'.join("!",@get_params).'"}';
|
||||||
|
}
|
24
Database/base.sql
Normal file
24
Database/base.sql
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
create tablespace deckard_space
|
||||||
|
OWNER deckard
|
||||||
|
LOCATION '\tmp\deckard';
|
||||||
|
create database deckard WITH
|
||||||
|
OWNER=deckard
|
||||||
|
TABLESPACE=deckard_space;
|
||||||
|
create table sessions (
|
||||||
|
sessionID UUID PRIMARY KEY,
|
||||||
|
sessionKey VARCHAR (256) NOT NULL,
|
||||||
|
last_update TIMESTAMP NOT NULL
|
||||||
|
);
|
||||||
|
create table locations (
|
||||||
|
locationID UUID PRIMARY KEY,
|
||||||
|
sessionID UUID REFERENCES sessions(sessionID),
|
||||||
|
locationType text CHECK (locationType = 'deck' or 'hand' or 'discard'),
|
||||||
|
showPublic boolean NOT NULL
|
||||||
|
);
|
||||||
|
create table cards (
|
||||||
|
cardID UUID PRIMARY KEY
|
||||||
|
sessionID UUID REFERENCES sessions(sessionID),
|
||||||
|
locationID UUID REFERENCES locations(locationID),
|
||||||
|
cardContent text NOT NULL,
|
||||||
|
position integer NOT NULL
|
||||||
|
);
|
24
README.md
Normal file
24
README.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
## Deckard and company
|
||||||
|
This project is an exansion on my original idea with [Deckard](https://labs.murkfall.net/bluesaxman/deckard), with this project I aim to make decard a fully over the internet multiplayer experience.
|
||||||
|
|
||||||
|
### Goals:
|
||||||
|
|
||||||
|
* Implement database backend
|
||||||
|
* Implement API backend
|
||||||
|
* Session creation
|
||||||
|
* Deck upload and sanitization
|
||||||
|
* Multipul Mixed and Separate decks per session
|
||||||
|
* Multipul Players per session.
|
||||||
|
* Multipul Discard piles
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
Not yet written
|
||||||
|
|
||||||
|
### Basic Play instructions
|
||||||
|
|
||||||
|
Not yet written
|
||||||
|
|
||||||
|
### Original Deckard project
|
||||||
|
|
||||||
|
[For developing new decks](https://labs.murkfall.net/bluesaxman/deckard)
|
126
UI/css.css
Normal file
126
UI/css.css
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
*|* {
|
||||||
|
margin:0;
|
||||||
|
padding:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content:center;
|
||||||
|
align-items:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#disc {
|
||||||
|
display:block;
|
||||||
|
width:80%;
|
||||||
|
min-height:60%;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#menu {
|
||||||
|
display:flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#display {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: stretch;
|
||||||
|
height: 80vh;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#deck, #deckDisp {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#deck {
|
||||||
|
width: 50%;
|
||||||
|
max-width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#hand {
|
||||||
|
display: flex;
|
||||||
|
width: 50%;
|
||||||
|
max-width: 50%;
|
||||||
|
overflow: auto;
|
||||||
|
justify-content: left;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library {
|
||||||
|
background: rgba(200,200,200,1);
|
||||||
|
text-align:left;
|
||||||
|
margin:5px 5px -15px 5px;
|
||||||
|
padding:5px 5px 20px 5px;
|
||||||
|
min-height:10%;
|
||||||
|
border:solid 1px rgba(140,140,140,1);
|
||||||
|
border-radius:10px 10px 0px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
display: inline-block;
|
||||||
|
background: rgba(150,150,150,0.7);
|
||||||
|
user-select: none;
|
||||||
|
margin:1px;
|
||||||
|
padding:2px;
|
||||||
|
border:solid 1px rgba(90,90,90,0.5);
|
||||||
|
border-radius:5px;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover {
|
||||||
|
background: rgba(140,140,140,1);
|
||||||
|
margin:0px;
|
||||||
|
padding:3px;
|
||||||
|
transition:0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog_back {
|
||||||
|
position:absolute;
|
||||||
|
display:flex;
|
||||||
|
justify-content:center;
|
||||||
|
align-items:center;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
width:100%;
|
||||||
|
height:100%;
|
||||||
|
background: rgba(0,0,0,0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog_window {
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
background: rgba(255,255,255,1);
|
||||||
|
border-radius:5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog_title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: rgba(0,0,0,0.3);
|
||||||
|
padding: 0px 0px 0px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog_content {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
height: 3in;
|
||||||
|
width: 2in;
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px rgba(0,0,0,1) solid;
|
||||||
|
border-radius: 5px;
|
||||||
|
min-height: 3in;
|
||||||
|
min-width: 2in;
|
||||||
|
max-height:3in;
|
||||||
|
max-width:5in;
|
||||||
|
overflow: auto;
|
||||||
|
background: rgb(216, 206, 184);
|
||||||
|
}
|
107
UI/index.html
Normal file
107
UI/index.html
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Deckard</title>
|
||||||
|
<link href="css.css" rel="stylesheet">
|
||||||
|
<script src="https://labs.murkfall.net/bluesaxman/blue.js/raw/master/libs/bluecore.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Welcome to Deckard<h1>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
window.deck = [];
|
||||||
|
window.hand = [];
|
||||||
|
|
||||||
|
function updateEvent() {
|
||||||
|
window.UI.deck.innerHTML = "";
|
||||||
|
window.UI.hand.innerHTML = "";
|
||||||
|
var theDeck = elementPlace("#deck","deckDisp","card","div");
|
||||||
|
theDeck.innerHTML = "<div>"+window.deck.length+"</div>";
|
||||||
|
window.hand.forEach(function (card,index) {
|
||||||
|
if (index < 10) {
|
||||||
|
var currentCard = elementPlace("#hand",null,"card","li");
|
||||||
|
currentCard.innerHTML = card;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateDeck(DDF) {
|
||||||
|
var cards = [""];
|
||||||
|
for (var attribute in DDF) {
|
||||||
|
var tempcards = [];
|
||||||
|
cards.forEach( function (current) {
|
||||||
|
DDF[attribute].forEach( function (value) {
|
||||||
|
tempcards.push(current+value);
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
cards = tempcards.slice();
|
||||||
|
}
|
||||||
|
return cards;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shuffleDeck(deck) {
|
||||||
|
deck.forEach(function (card,index) {
|
||||||
|
var swapCardIndex = Math.floor( Math.random() * deck.length );
|
||||||
|
var swapCard = deck[swapCardIndex];
|
||||||
|
deck[swapCardIndex] = card;
|
||||||
|
deck[index] = swapCard;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deckFromJSON(ourFile) {
|
||||||
|
if ( Array.isArray(ourFile) ) {
|
||||||
|
ourFile.forEach(function (deck) {
|
||||||
|
window.deck = window.deck.concat(generateDeck(deck));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
window.deck = window.deck.concat(generateDeck(ourFile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elementPlace("body","menu",null,"div");
|
||||||
|
buttonAdd("#menu","upload","Upload Deck", function () {
|
||||||
|
popupDialog("deckLoader","Select Deck definition File",true,inputDialog,{"inputType":"file"},function (f) {
|
||||||
|
var myFile = new FileReader();
|
||||||
|
myFile.onload = function (file) {
|
||||||
|
// Probably validate the file somehow befor eating it
|
||||||
|
var ourFile = JSON.parse(file.target.result);
|
||||||
|
window.deck = [];
|
||||||
|
deckFromJSON(ourFile);
|
||||||
|
window.hand = [];
|
||||||
|
updateEvent();
|
||||||
|
}
|
||||||
|
myFile.readAsText(f[0]);
|
||||||
|
});
|
||||||
|
},null,"div");
|
||||||
|
buttonAdd("#menu","add","Add Deck", function () {
|
||||||
|
popupDialog("deckLoader","Select Deck definition to add",true,inputDialog,{"inputType":"file"},function (f) {
|
||||||
|
var myFile = new FileReader();
|
||||||
|
myFile.onload = function (file) {
|
||||||
|
var ourFile = JSON.parse(file.target.result);
|
||||||
|
deckFromJSON(ourFile);
|
||||||
|
updateEvent();
|
||||||
|
}
|
||||||
|
myFile.readAsText(f[0]);
|
||||||
|
});
|
||||||
|
}, null, "div");
|
||||||
|
buttonAdd("#menu","shuffle", "Shuffle Deck", function () {
|
||||||
|
shuffleDeck(window.deck);
|
||||||
|
}, null, "div");
|
||||||
|
buttonAdd("#menu","shuffleAll", "Shuffle Whole Deck", function () {
|
||||||
|
window.deck = window.deck.concat(window.hand);
|
||||||
|
window.hand = [];
|
||||||
|
shuffleDeck(window.deck);
|
||||||
|
updateEvent();
|
||||||
|
}, null, "div");
|
||||||
|
buttonAdd("#menu","draw", "Draw Card", function () {
|
||||||
|
if (window.deck.length > 0) {
|
||||||
|
window.hand.unshift(window.deck.shift());
|
||||||
|
updateEvent();
|
||||||
|
}
|
||||||
|
}, null, "div");
|
||||||
|
elementPlace("body","display",null,"div");
|
||||||
|
window.UI = {};
|
||||||
|
window.UI.deck = elementPlace("#display","deck",null,"div");
|
||||||
|
window.UI.hand = elementPlace("#display","hand",null,"ol");
|
||||||
|
</script>
|
||||||
|
</html>
|
Loading…
x
Reference in New Issue
Block a user