Move conf outside of webroot; fix func invocation
[mudd.git] / mud.php
1 <?php
2
3 abstract class model {
4
5 private $config_file = '/etc/mud.ini';
6 private $db;
7
8 public function __construct() {
9 $config = parse_ini_file($this->config_file, true);
10 $this->db = new mysqli(
11 $config['database']['domain'],
12 $config['database']['user'],
13 $config['database']['password'],
14 $config['database']['database']);
15 if (mysqli_connect_errno()) {
16 echo "Problem connecting to database: ";
17 echo mysqli_connect_error();
18 exit();
19 }
20 }
21
22 public function query() {
23 $args = func_get_args();
24 $statement = $this->db->prepare(array_shift($args));
25 #if ($args) call_user_func_array(array($statement, 'bind_param'), &$args);
26 if ($args) call_user_func_array(array($statement, 'bind_param'), $args);
27 $statement->execute();
28 $statement->store_result();
29 $statement->store_result();
30 $data = $statement->result_metadata();
31 $return = $row = $fields = array();
32 $fields[0] = &$statement;
33 while($field = $data->fetch_field())
34 $fields[] = &$row[$field->name];
35 call_user_func_array("mysqli_stmt_bind_result", $fields);
36 $i = 0;
37 while ($statement->fetch()) {
38 foreach ($row as $key=>$value) $return[$i][$key] = $value;
39 $i++;
40 }
41 $statement->free_result();
42 return $return;
43 }
44
45 public function insert() {
46 $args = func_get_args();
47 $statement = $this->db->prepare(array_shift($args));
48 call_user_func_array(array($statement, 'bind_param'), $args);
49 $statement->execute();
50 return $this->db->insert_id;
51 }
52
53 public function update() {
54 $args = func_get_args();
55 $statement = $this->db->prepare(array_shift($args));
56 call_user_func_array(array($statement, 'bind_param'), $args);
57 $statement->execute();
58 return $this->db->insert_id;
59 }
60
61 }
62
63 class universe {
64 public $rows = 30;
65 public $columns = 30;
66 public $rooms = array();
67
68 public function __construct($db) {
69 $rooms = $db->query('SELECT id,x,y,state FROM rooms');
70 if ($rooms) {
71 foreach ($rooms as $room) {
72 $this->rooms[$room['x']][$room['y']] =
73 array('id' => $room['id'], 'state' => $room['state']);
74 }
75 } else {
76 $sql = 'INSERT INTO rooms (x,y,state) VALUES(?,?,?)';
77 $types = 'iii';
78 for ($x = 0; $x < $this->columns; $x++) {
79 $column = array();
80 for ($y = 0; $y < $this->rows; $y++) {
81 $state = (rand(0, 9) < 2 ? true : false);
82 $id = $db->insert($sql, $types, $x, $y, $state);
83 $this->rooms[$x][$y] =
84 array('id' => $id, 'state' => $state);
85 }
86 }
87 }
88 }
89
90 public function serialize() {
91 $serial = array();
92 for ($x = 0; $x < $this->columns; $x++) {
93 for ($y = 0; $y < $this->rows; $y++) {
94 $serial[$x][$y] = $this->rooms[$x][$y]['state'];
95 }
96 }
97 return $serial;
98 }
99
100 }
101
102 class player {
103
104 public $id;
105 public $name;
106 public $room;
107 public $x;
108 public $y;
109
110 public function __construct($db) {
111 $player = $db->query(
112 'SELECT players.id, players.name, players.room, rooms.x, rooms.y'
113 . ' FROM players LEFT JOIN rooms ON players.room = rooms.id'
114 . ' WHERE players.id = ?',
115 'i', $_SESSION['id']
116 );
117 $player = $player[0];
118 $this->id = $player['id'];
119 $this->name = $player['name'];
120 $this->room = $player['room'];
121 $this->x = $player['x'];
122 $this->y = $player['y'];
123 }
124
125 }
126
127 class mud extends model {
128
129 private $universe;
130
131 public function __construct() {
132 parent::__construct();
133 session_start();
134 $this->universe = new universe($this);
135 }
136
137 private function join() {
138 $x = $y = 0;
139 do {
140 $x = rand(0, count($this->universe->rooms)-1);
141 $y = rand(0, count($this->universe->rooms[0])-1);
142 } while ($this->universe->rooms[$x][$y]['state']);
143 $id = $this->insert('INSERT INTO players (name,room) VALUES(?,?)',
144 'si', $_GET['name'], $this->universe->rooms[$x][$y]['id']);
145 $_SESSION['id'] = $id;
146 //$others = $this->query('SELECT id, room FROM players WHERE id != ?', 'i', $this->player->id);
147 return array('x' => $x, 'y' => $y, 'id' => $id, 'poll' => $this->poll());
148 }
149
150 private function yell($msg) {
151 if (!$msg) $this->error('400', 'expected `msg` field');
152 $this->insert(
153 'INSERT INTO messages (message,type,room,source) VALUES(?,?,?,?)',
154 'ssii', $msg, 'yell', $this->player->room, $this->player->id);
155 }
156
157 private function say($msg) {
158 if (!$msg) $this->error('400', 'expected `dest` field');
159 $this->insert(
160 'INSERT INTO messages (message,type,room,source) VALUES(?,?,?,?)',
161 'ssii', $msg, 'say', $this->player->room, $this->player->id);
162 }
163
164 private function tell($dest, $msg) {
165 if (!$dest || !$msg) $this->error('400', 'expected `dest` and `msg` fields');
166 $player = $this->query('SELECT id FROM players where name = ?', 's', $dest);
167 if (!($player && $player['id']))
168 $this->error(400, "could not find a player with `name` == $dest");
169 $this->insert(
170 'INSERT INTO messages (message,type,destination,source) VALUES(?,?,?,?)',
171 'ssii', $msg, 'say', $player['id'], $this->player->id);
172 }
173
174 private function move($direction) {
175 $directions = array(
176 'north' => array('x' => $this->player->x, 'y' => $this->player->y-1),
177 'east' => array('x' => $this->player->x+1, 'y' => $this->player->y),
178 'south' => array('x' => $this->player->x, 'y' => $this->player->y+1),
179 'west' => array('x' => $this->player->x-1, 'y' => $this->player->y),
180 );
181 if ($directions[$direction]) {
182 $rooms = $this->query(
183 'SELECT id, state, description FROM rooms WHERE x = ? and y = ?',
184 'ii', $directions[$direction]['x'], $directions[$direction]['y']);
185 if ($rooms[0] && !$rooms[0]['state']) {
186 $this->update('UPDATE players SET room = ? WHERE players.id = ?',
187 'si', $rooms[0]['id'], $this->player->id);
188 return array('description' => $rooms[0]['description']);
189 } else {
190 $this->error(403, 'your path is blocked');
191 }
192 } else
193 $this->error(400, 'expected `direction` field with value {north|east|south|west}');
194 }
195
196 private function poll() {
197 $time = isset($_SESSION['last_polled']) ? $_SESSION['last_polled'] : 0;
198 $messages = $this->query(
199 'SELECT message, type, name, sent, players.id FROM messages LEFT JOIN players'
200 . ' ON destination = players.id'
201 . ' OR source = players.id'
202 . ' WHERE TIMESTAMPDIFF(MINUTE, sent, NOW()) < 5'
203 . ' AND UNIX_TIMESTAMP(sent) >= ?'
204 . ' AND (type = "yell"'
205 . ' OR (type = "tell" AND destination = ?)'
206 . ' OR (type = "say" AND messages.room = ?))',
207 'iii', $time, $this->player->id, $this->player->room);
208 $players = $this->query(
209 'SELECT players.id, name, x ,y '
210 . 'FROM players JOIN rooms '
211 . 'ON players.room = rooms.id');
212 $_SESSION['last_polled'] = time();
213 return array('messages' => $messages, 'players' => $players);
214 }
215
216 public function response($content) {
217 header('Content-Type: application/json');
218 echo json_encode($content);
219 }
220
221 public function command($cmd) {
222 if (!$_GET['cmd'])
223 $this->error(400, 'Missing command: expected `cmd` field');
224 if (($_GET['cmd'] != 'join' && $_GET['cmd'] != 'kick')
225 && $_GET['cmd'] != 'start' && !isset($_SESSION['id']))
226 $this->error(401, 'Missing user ID: please join first');
227 if (isset($_SESSION['id'])) $this->player = new player($this);
228 switch ($cmd) {
229 case 'start':
230 $this->response($this->universe->serialize());
231 break;
232 case 'join':
233 $this->response($this->join());
234 break;
235 case 'move':
236 $this->response($this->move($_GET['direction']));
237 break;
238 case 'tell':
239 $this->tell($_GET['msg']);
240 break;
241 case 'yell':
242 $this->yell($_GET['msg']);
243 break;
244 case 'say':
245 $this->say($_GET['msg']);
246 break;
247 case 'tell':
248 $this->tell($_GET['dest'], $_GET['msg']);
249 break;
250 case 'poll':
251 $this->response($this->poll());
252 break;
253 case 'kick':
254 $this->update('DELETE FROM players');
255 break;
256 default:
257 $this->error(400, 'unknown command');
258 }
259 }
260
261 private function error($status, $msg) {
262 header("HTTP/1.0 $status");
263 echo $msg;
264 exit;
265 }
266
267 }
268
269 $mud = new mud();
270 $mud->command($_GET['cmd']);
271
272 ?>