PHP Classes

File: test-cli.example.game.php

Recommend this page to a friend!
  Classes of JImmy Bo   PHP Non Blocking CLI   test-cli.example.game.php   Download  
File: test-cli.example.game.php
Role: Example script
Content type: text/plain
Description: RPG MUD-like dungeon adventure game example with NPCs and Entities
Class: PHP Non Blocking CLI
Implement CLI scripts that do not block the output
Author: By
Last change: code cleanup
Date: 11 months ago
Size: 21,580 bytes
 

Contents

Class file image Download
<?php // example using the NonBlockingCLI class // RPG MUD-like dungeon adventure game example with NPCs and Entities include_once('class.nb_cli_1.0.php'); // BEGIN :: AREA DEFINITION $rooms_arr = array( "room1" => array( "name" => "Room 1", "desc" => "This is room 1", "exits" => array( "north" => "room2", "south" => "room3", "east" => "room4", "west" => "room5" ), "starting_room" => true // our player starting room ), "room2" => array( "name" => "Room 2", "desc" => "This is room 2", "exits" => array( "south" => "room1" ) ), "room3" => array( "name" => "Room 3", "desc" => "This is room 3", "exits" => array( "north" => "room1" ) ), "room4" => array( "name" => "Room 4", "desc" => "This is room 4", "exits" => array( "west" => "room1" ) ), "room5" => array( "name" => "Room 5", "desc" => "This is room 5", "exits" => array( "east" => "room1" ) ) ); class c_room { public $uid; public $data = []; public function __construct() { $this->uid = uniqid(); } public function set($key, $value) { $this->data[$key] = $value; } public function get($key) { if (isset($this->data[$key]) ) return $this->data[$key]; } } class c_rooms { private $rooms = array(); public function __construct() { } public function get_start() { foreach($this->rooms as $room_uid => $room) { if($room->get('starting_room') == true) { $starting_rooms[] = $room; } } if(count($starting_rooms) == 0) { return false; } // return a random starting room return $starting_rooms[array_rand($starting_rooms)]->uid; } // get random room public function get_random() { return $this->rooms[array_rand($this->rooms)]; } public function show() { // loop through each one and print_r it foreach($this->rooms as $room_uid => $room) { print_r($room); } } public function get_exits($uid) { if(isset($this->rooms[$uid]) == false) return false; return $this->rooms[$uid]->get('exits_uid'); } public function add() { $r = new c_room(); $this->rooms[$r->uid] = $r; return $this->rooms[$r->uid]; } public function get($uid) { if(isset($this->rooms[$uid]) == false) { return false; } return $this->rooms[$uid]; } public function remove($uid) { if(isset($this->rooms[$uid]) == false) { return false; } unset($this->rooms[$uid]); return true; } // add from array public function add_from_array($rooms_arr) { // use array $rooms_arr as reference foreach($rooms_arr as $read_id => $room_data) { $r = $this->add(); $r->set('read_id', $read_id); foreach($room_data as $key => $value) { $r->set($key, $value); } } // now go through and link up exits and add a exits_uid array to rooms with exits foreach($this->rooms as $room_uid => $room) { if(isset($room->data['exits'])) { $exits_uid = []; foreach($room->data['exits'] as $exit_direction => $read_id) { // $exits_uid[$exit_direction] = $this->get($exit_room_uid)->uid; // find room that matches read_id foreach($this->rooms as $room_uid2 => $room2) { if($room2->get('read_id') == $read_id) { $exits_uid[$exit_direction] = $room2->uid; } } } $room->set('exits_uid', $exits_uid); } } } } class c_entity { public $uid; public $data = []; public $c_entities; private $stdin = NULL; private $stdout = NULL; public function __construct($c_entities) { $this->uid = uniqid(); $this->set("cur room", -1); $this->c_entities = $c_entities; } public function set($key, $value) { $this->data[$key] = $value; } public function get($key) { if (isset($this->data[$key]) ) return $this->data[$key]; } public function set_local() { $this->set("local", true); // set non-blocking mode for STDIN $this->stdin = fopen('php://stdin', 'r'); $this->stdout = fopen('php://stdout', 'w'); } public function out($msg) { if($this->get("local") == true) fwrite($this->stdout, $msg); } public function do_prompt() { $this->out("\r\n(Type 'help' or '?' for help) > "); } } class c_entities { public $entities = array(); public function __construct() { } public function add() { $e = new c_entity($this); $this->entities[$e->uid] = $e; return $this->entities[$e->uid]; } public function get($uid) { if(isset($this->entities[$uid]) == false) { return false; } return $this->entities[$uid]; } public function get_all_in_room($room_uid) { $entities_in_room = []; foreach($this->entities as $entity_uid => $entity) if($entity->get("cur room") == $room_uid) $entities_in_room[] = $entity; return $entities_in_room; } public function get_others_in_room($room_uid, $src_entity_uid) { $entities_in_room = $this->get_all_in_room($room_uid); // remove if uid matches src_entity_uid foreach($entities_in_room as $entity_uid => $entity) if($entity->uid == $src_entity_uid) unset($entities_in_room[$entity_uid]); return $entities_in_room; } public function remove($uid) { if(isset($this->entities[$uid]) == false) { return false; } unset($this->entities[$uid]); return true; } } ### BEGIN: non-blocking cli class related ### class my_plugin extends nb_plugin { private $stdin; private $stdout; public $data = []; // helper classes public $c_rooms; public $c_entity; // this player entity in c_entities array // use this to ref main player public $c_entities; // public function __construct() { // Set non-blocking mode for STDOUT stream_set_blocking(STDOUT, false); // Set non-blocking mode for STDIN stream_set_blocking(STDIN, false); $this->stdin = fopen('php://stdin', 'r'); $this->stdout = fopen('php://stdout', 'w'); $this->c_rooms = new c_rooms(); $this->c_entities = new c_entities(); $this->c_entity = $this->c_entities->add(); // $this->c_entity->set("local", true); $this->c_entity->set_local(); $this->c_entity->set("name", "Test Player"); $this->c_entity->set("desc", "This is a test player."); // $this->welcome(); } // set starting room public function set_starting_room($uid) { echo "set_starting_room: " . $uid . "\n"; $this->c_entity->set("cur room", $uid); } // create and place a random npc public function create_random_npc() { $npc = $this->c_entities->add(); // pick a random name // $name = "NPC " . rand(1, 1000); $npc->set("name", $name); $npc->set("desc", "This is a test NPC."); $npc->set("cur room", $this->c_rooms->get_random()->uid); $npc->set("npc", true); } public function random_move_npcs() { // echo "random_move_npcs\n"; // go through each non local entity and move it randomly foreach($this->c_entities->entities as $entity_uid => $entity) { // echo "random_move_npcs: " . $entity->get("name") . "\n"; if($entity->get("npc") == true) { // echo "random_move_npcs: " . $entity->get("name") . "\n"; // random 10% chance of moving if(rand(1, 10) != 1) { continue; } $this->move_dir($this->expand_cardinal(array_rand($this->c_rooms->get_exits($entity->get("cur room")))), $entity); } } } public function set($key, $value) { $this->data[$key] = $value; } public function get($key) { if (isset($this->data[$key]) ) return $this->data[$key]; } public function out($msg) { // moved to entity so our npcs can process messages fwrite($this->stdout, $msg); } public function move_dir($dir, $entity = null) { if($entity == null) { $entity = $this->c_entity; } $cur_room_uid = $entity->get("cur room"); $exits = $this->c_rooms->get_exits($cur_room_uid); if(isset($exits[$dir]) == false) { // if is local player, show message // if($entity->get("local") == true) // $this->out("You can't go that way.\n"); $entity->out("You can't go that way.\n"); $entity->do_prompt(); return false; } // send a message to current room local player that this entity is leaving $entities_in_room = $this->c_entities->get_all_in_room($cur_room_uid); foreach($entities_in_room as $entity_uid => $entity2) { /* if($entity2->get("local") == true) { $this->c_entity->out($entity->get("name") . " leaves the room to the " . $dir . ".\n"); } */ // if entity not c_entity (match by uid) if($entity2->uid != $entity->uid) { $entity2->out($entity->get("name") . " leaves the room to the " . $dir . ".\n"); $entity2->do_prompt(); } } $entity->set("cur room", $exits[$dir]); // show message $entity->out("You move " . $dir . ".\n\n"); // get all entities in room // if our moving entity isn't a local player, and it is moving into the room as the local player, emit the message to local player $entities_in_room = $this->c_entities->get_all_in_room($exits[$dir]); foreach($entities_in_room as $entity_uid => $entity2) { /* if($entity2->get("local") == true) { $this->out($entity->get("name") . " enters the room from the " . $dir . ".\n"); } */ // if entity not c_entity (match by uid) if($entity2->uid != $entity->uid) { $entity2->out($entity->get("name") . " enters the room.\n"); // should find opp direction and output that $entity2->do_prompt(); } } return true; } public function do_look() { $cur_room_uid = $this->c_entity->get("cur room"); $cur_room = $this->c_rooms->get($cur_room_uid); // output room name and desc $this->c_entity->out("\n"); $this->c_entity->out("You are in " . $cur_room->get('name') . "\n"); $this->c_entity->out("-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-\n"); $this->c_entity->out($cur_room->get('desc') . "\n"); $this->c_entity->out("Exits: "); // get exits foreach($cur_room->get('exits') as $exit_direction => $exit_room_uid) { $this->out($exit_direction . " "); } $this->out("\n"); // get entities in room $entities_in_room = $this->c_entities->get_all_in_room($cur_room_uid); foreach($entities_in_room as $entity_uid => $entity) { // if entity not c_entity (match by uid) if($entity->uid != $this->c_entity->uid) { $this->c_entity->out($entity->get("name") . " is here.\n"); } } $this->c_entity->do_prompt(); } public function expand_cardinal($dir) { switch($dir) { case 'n': return 'north'; case 's': return 'south'; case 'e': return 'east'; case 'w': return 'west'; case 'u': return 'up'; case 'd': return 'down'; case 'ne': return 'northeast'; case 'nw': return 'northwest'; case 'se': return 'southeast'; case 'sw': return 'southwest'; } return $dir; } public function cmd_is_dir($dir) { switch($dir) { case 'north': case 'n': case 'south': case 's': case 'east': case 'e': case 'west': case 'w': case 'up': case 'u': case 'down': case 'd': case 'northeast': case 'ne': case 'northwest': case 'nw': case 'southeast': case 'se': case 'southwest': case 'sw': return true; } } public function handleCommand($command) { // trim $command = trim($command); // convert cardinal $command = $this->expand_cardinal($command); // if $command is an available exit direction, move that way $cur_room_uid = $this->c_entity->get("cur room"); $exits = $this->c_rooms->get_exits($cur_room_uid); if($this->cmd_is_dir($command)) if(isset($exits[$command])) { $this->move_dir($command); $this->do_look(); } else { $this->c_entity->out("You can't go that way.\n"); $this->c_entity->do_prompt(); } if(!$this->cmd_is_dir($command)) { // process commands from user when they press enter switch ($command) { case 'help': case 'h': case '?': $help = "\n\n"; $help .= "---\n"; $help .= "Showing help for *TestPluginGame*\n"; $help .= "Available commands:\n"; $help .= " directions: as specified by the room you are in\n"; $help .= " look: look around the room\n"; $help .= " show rooms: show all rooms (debug)\n"; $help .= " quit: quit the game\n"; $help .= "---\n\n"; $this->c_entity->out($help); $this->c_entity->do_prompt(); break; case 'show rooms': $this->c_rooms->show(); $this->c_entity->do_prompt(); break; case 'look': case 'l': $this->do_look(); break; default: if($command != "quit") $this->out("Invalid command. Type 'help' for a list of commands.\n"); break; } // end switch } } public function welcome() { // triggered by main loop when code requests a welcome $welcome = "\n\n\n\n"; $welcome .= "-- NonBlockingCLI test server --\n"; $welcome .= "Welcome to the NonBlockingCLI test server.\n"; $welcome .= "Type 'h' or 'help' for a list of commands.\n"; $welcome .= "Type 'quit' to exit.\n"; $welcome .= "\n\n"; $this->c_entity->out($welcome); $this->do_look(); } public function run() { // this loop runs at main class speed $this->run_loop_1(); // output current time every second $this->run_ai_movement_loop(); // run every second // .. can add other loops. Use run_loop_1 as an example. } public function run_ai_movement_loop() { $loop_key = 2; // for $this->next_run and $this->loop_running if (!isset($this->next_run[$loop_key])) { $this->next_run[$loop_key] = 0.0; // initialize // example of potential values: 1 second = 1.0, 1.5 seconds = 1.5, etc. $this->loop_running[$loop_key] = false; } if ($this->loop_running[$loop_key] || microtime(true) < $this->next_run[$loop_key]) return; $this->loop_running[$loop_key] = true; // do: // echo "ai loop\r\n"; $this->random_move_npcs(); // set pace of plugin loop (runs independent of main loop) // store our next run time in a variable $this->next_run[$loop_key] = microtime(true) + 5.0; // 1 second = 1.0, 1.5 seconds = 1.5, etc. // set loop as not running $this->loop_running[$loop_key] = false; } public function run_loop_1() { $loop_key = 1; // for $this->next_run and $this->loop_running // we want to take a slot in next_run in case we have other loops. if (!isset($this->next_run[$loop_key])) { $this->next_run[$loop_key] = 0.0; // initialize // example of potential values: 1 second = 1.0, 1.5 seconds = 1.5, etc. $this->loop_running[$loop_key] = false; } // check if we should run this time if ($this->loop_running[$loop_key] || microtime(true) < $this->next_run[$loop_key]) { return; } // set loop as running $this->loop_running[$loop_key] = true; $output = "Current time: " . date('Y-m-d H:i:s') . "\n"; $this->c_entity->out($output); $this->c_entity->do_prompt(); // set pace of plugin loop (runs independent of main loop) // store our next run time in a variable $this->next_run[$loop_key] = microtime(true) + 10.0; // 1 second = 1.0, 1.5 seconds = 1.5, etc. // set loop as not running $this->loop_running[$loop_key] = false; } } ## EXAMPLE: ## // create plugins $plugin_a = new my_plugin(); // create main object that controls the plugins $cli = new nb_cli(); // add plugins to main controller $cli->add_plugin($plugin_a); // add rooms from array $plugin_a->c_rooms->add_from_array($rooms_arr); $plugin_a->set_starting_room($plugin_a->c_rooms->get_start()); // create some random npcs for ($i = 0; $i < 10; $i++) $plugin_a->create_random_npc(); // run our main controller which will run the plugins welcome() functions $cli->welcome(); // run our main controller which will run the plugins run() functions in a loop // maximum speed is set in main controller, but each plugin can time their own runs $cli->run(); ?>