<?php
/*
Copyright (C) 2004-2009 Kestas J. Kuliukas
This file is part of webDiplomacy.
webDiplomacy is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
webDiplomacy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with webDiplomacy. If not, see <http://www.gnu.org/licenses/>.
*/
defined('IN_CODE') or die('This script can not be run by itself.');
require_once('board/orders/order.php');
require_once('board/orders/diplomacy.php');
require_once('board/orders/retreats.php');
require_once('board/orders/builds.php');
/**
* A class for submission/retrieval of orders; usable internally or externally
*
* __construct
* => This data is required to load: [gameID, userID, memberID, turn, phase, countryID]
* This must either be in the context of a GameMaster account, a loaded Game/User/userMember set, or a JSON token.
*
* loadOrders
* => Current order data can then be loaded via a JSON token [tokenKey,{orderID,completeStatus,{data}}]
* , if provided with the above token. Or from the DB.
*
* getToken / getOrders
* Once current order data is loaded:
* <= [gameID, ...] token can be returned
* <= [tokenKey,{orderID,completeStatus,{data}}] can be returned for the given user.
*
* setOrders
* => To alter orders once loaded this data is provided: lock,{orderID,{input}}
* <= {orderID, completeStatus, error} will be returned
*
* @package Board
* @subpackage Orders
*/
class OrderInterface
{
public static function newBoard() {
global $Game, $User, $Member;
return self::newContext($Game, $Member, $User);
}
public static function newContext(Game $Game, userMember $Member, User $User) {
$OI = $Game->Variant->OrderInterface($Game->id, $Game->Variant->id, $User->id, $Member->id, $Game->turn, $Game->phase, $Member->countryID,
$Member->orderStatus, $Game->processTime+6*60*60);
return $OI;
}
public static function newJSON($key, $json) {
$inContext = (array)json_decode($json);
$authContext = self::getContext($inContext);
$inContext = $authContext['context'];
if( $authContext['key'] != $key )
throw new Exception("JSON token given is invalid");
require_once('lib/variant.php');
$Variant=libVariant::loadFromVariantID($inContext['variantID']);
libVariant::setGlobals($Variant);
require_once('objects/basic/set.php');
$OI = $Variant->OrderInterface($inContext['gameID'],$inContext['variantID'],$inContext['userID'],$inContext['memberID'],
$inContext['turn'],$inContext['phase'],$inContext['countryID'],
new setMemberOrderStatus($inContext['orderStatus']), $inContext['tokenExpireTime'], $inContext['maxOrderID']);
return $OI;
}
public $gameID;
protected $variantID;
protected $userID;
protected $memberID;
protected $turn;
protected $phase;
protected $countryID;
public $orderStatus;
protected $tokenExpireTime;
protected $maxOrderID;
public function __construct($gameID, $variantID, $userID, $memberID, $turn, $phase, $countryID,
setMemberOrderStatus $orderStatus, $tokenExpireTime, $maxOrderID=false)
{
$this->gameID=(int)$gameID;
$this->variantID=(int)$variantID;
$this->userID=(int)$userID;
$this->memberID=(int)$memberID;
$this->turn=(int)$turn;
$this->phase=$phase;
$this->countryID=$countryID;
$this->orderStatus=$orderStatus;
$this->tokenExpireTime=$tokenExpireTime;
$this->maxOrderID=$maxOrderID;
}
protected $Orders;
public function load()
{
global $DB;
$DB->sql_put("SELECT * FROM wD_Members WHERE gameID = ".$this->gameID." AND countryID=".$this->countryID." ".UPDATE);
$tabl = $DB->sql_tabl("SELECT id, type, unitID, toTerrID, fromTerrID, viaConvoy
FROM wD_Orders WHERE gameID = ".$this->gameID." AND countryID=".$this->countryID);
$this->Orders = array();
$maxOrderID=0;
while ( $row = $DB->tabl_hash($tabl) )
{
if( $row['id'] > $maxOrderID ) $maxOrderID = $row['id'];
$Order = userOrder::load($this->phase,$row['id'],$this->gameID, $this->countryID);
$Order->loadFromDB($row);
$this->Orders[] = $Order;
}
list($checkTurn, $checkPhase) = $DB->sql_row("SELECT turn, phase FROM wD_Games WHERE id=".$this->gameID);
if( $checkTurn != $this->turn || $checkPhase != $this->phase )
throw new Exception("The game has moved on, you can no longer alter these orders, please refresh.");
if( $this->maxOrderID == false ) $this->maxOrderID = $maxOrderID;
//elseif( $this->maxOrderID < $maxOrderID )
//if( $this->tokenExpireTime < time() ) throw new Exception("The game has moved on, you can no longer alter these orders, please refresh.");
}
public function set($orderUpdates)
{
if( $this->orderStatus->Ready ) return;
$this->log($orderUpdates);
$orderUpdates = json_decode($orderUpdates);
foreach($orderUpdates as $orderUpdate)
{
$orderUpdate = (array)$orderUpdate;
foreach($this->Orders as $Order)
if( $Order->id == $orderUpdate['id'] )
$Order->loadFromInput($orderUpdate);
}
}
protected function log($logData)
{
$orderlogDirectory = Config::orderlogDirectory();
if ( false === $orderlogDirectory ) return;
require_once('objects/game.php');
$directory = libCache::dirID($orderlogDirectory, $this->gameID, true);
$file = $this->countryID.'.txt';
if ( ! ($orderLog = fopen($directory.'/'.$file, 'a')) )
trigger_error("Couldn't open order log file.");
if( !fwrite($orderLog, 'Time: '.gmdate("M d Y H:i:s")." (UTC+0)\n".$logData."\n\n") )
trigger_error("Couldn't write to order log file.");
fflush($orderLog) or trigger_error("Couldn't write to order log file.");
fclose($orderLog);
}
protected $results = array('orders'=>array(),'notice'=>'','statusIcon'=>'','statusText'=>'','invalid'=>false);
public function validate() {
if( count($this->Orders)==0 )
$this->orderStatus->None=true;
$complete=true;
foreach($this->Orders as $Order)
{
$Order->validate();
$result = $Order->results();
if( $complete && $result['status'] != 'Complete' )
$complete=false;
if( $result['status'] == 'Invalid' )
{
$complete=false;
$this->results['invalid'] = true;
}
$this->results['orders'][$Order->id] = $result;
}
$this->orderStatus->Completed = $complete;
return $this->results;
}
public function readyToggle() {
global $Member, $DB;
if( !$this->orderStatus->Ready )
{
if( !$this->orderStatus->Completed )
$this->results['notice'] .= ' Could not set to ready, orders not complete and valid.';
else
$this->orderStatus->Ready=true;
}
else
$this->orderStatus->Ready = false;
return $this->orderStatus->Ready;
}
public function writeOrders() {
$updated=false;
foreach($this->Orders as $Order)
if( $Order->commit() )
$updated=true;
if( $updated )
$this->orderStatus->Saved=true;
}
public function writeOrderStatus() {
global $DB, $Member;
$this->results['statusIcon']=$this->orderStatus->icon();
$this->results['statusText']=$this->orderStatus->iconText();
if( $this->orderStatus->updated )
{
if( isset($Member) && $Member instanceof Member && $Member->id == $this->memberID )
$Member->orderStatus = $this->orderStatus;
$DB->sql_put("UPDATE wD_Members SET orderStatus = '".$this->orderStatus."' WHERE id = ".$this->memberID);
$newContext = $this->getContext($this);
$this->results['newContext'] = $newContext['context'];
$this->results['newContextKey'] = $newContext['key'];
}
}
public function getResults() {
return $this->results;
}
protected static $contextVars=array('gameID','userID','memberID','variantID','turn','phase','countryID','tokenExpireTime','maxOrderID');
public static function getContext($contextOf) {
$context=array();
foreach($contextOf as $name=>$val)
{
if(!in_array($name, self::$contextVars)) continue;
$context[$name] = $val;
}
if( is_array($contextOf) )
$context['orderStatus'] = ''.$contextOf['orderStatus'];
else
$context['orderStatus'] = ''.$contextOf->orderStatus;
$json=json_encode($context);
return array('context'=>$context, 'json'=>$json, 'key'=>md5(Config::$jsonSecret.$json).sha1(Config::$jsonSecret.$json));
}
protected function jsContextVars() {
$context = self::getContext($this);
libHTML::$footerScript[] = '
context='.$context['json'].';
contextKey="'.$context['key'].'";
ordersData = '.json_encode($this->Orders).';
';
}
protected function jsLoadBoard() {
libHTML::$footerIncludes[] = 'board/model.js';
libHTML::$footerIncludes[] = 'board/load.js';
libHTML::$footerIncludes[] = 'orders/order.js';
libHTML::$footerIncludes[] = 'orders/phase'.$this->phase.'.js';
libHTML::$footerIncludes[] = '../'.libVariant::$Variant->territoriesJSONFile();
libHTML::$footerScript[] = '
loadTerritories();
loadBoardTurnData();
loadModel();
loadBoard();
loadOrdersModel();
loadOrdersForm();
loadOrdersPhase();
';
}
protected function jsInitForm() {
libHTML::$footerIncludes[] = 'orders/form.js';
libHTML::$footerScript[] = '
OrdersHTML.formInit(context, contextKey);
';
}
protected function jsLiveBoardData() {
$jsonBoardDataFile=Game::mapFilename($this->gameID, ($this->phase=='Diplomacy'?$this->turn-1:$this->turn), 'json');
if( !file_exists($jsonBoardDataFile) )
$jsonBoardDataFile='map.php?gameID='.$this->gameID.'&turn='.$this->turn.'&phase='.$this->phase.'&mapType=json'.(defined('DATC')?'&DATC=1':'').'&nocache='.rand(0,1000);
else
$jsonBoardDataFile.='?phase='.$this->phase.'&nocache='.rand(0,10000);
return '<script type="text/javascript" src="'.STATICSRV.$jsonBoardDataFile.'"></script>';
}
public function jsHTML() {
$this->jsContextVars();
$this->jsLoadBoard();
$this->jsInitForm();
return $this->jsLiveBoardData();
}
public function html()
{
//method="post" action="board.php?gameID='.$this->gameID.'#orders"
$html = $this->jsHTML();
$html .= '
<form id="orderFormElement" onsubmit="return false;">
<a name="orders"></a><table class="orders">';
$alternate = false;
foreach($this->Orders as $Order)
{
$alternate = ! $alternate;
$html .= '<tr class="barAlt'.($alternate ? '1' : '2').'">
<td class="uniticon"><span id="orderID'.$Order->id.'UnitIconArea"></span></td>
<td class="order"><div id="orderID'.$Order->id.'">Loading order...</div></td>
</tr>';
}
$html .= "</table>".'
<div style="text-align:center;"><span id="ordersNoticeArea'.$this->memberID.'"></span>
<input id="UpdateButton'.$this->memberID.'" type="Submit" class="form-submit" name="'.
'Update" value="Save" disabled />
<input id="FinalizeButton'.$this->memberID.'" type="Submit" class="form-submit" name="'.
($this->orderStatus->Ready?'Not ready':'Ready').'" value="'.($this->orderStatus->Ready?'Not ready':'Ready').'" disabled />
</div>
</form>';
return $html;
}
}
?>