fetch(PDO::FETCH_ASSOC)) { $row['locationname'] = str_replace(',', ';', $row['locationname']); // comma probably not the best sep here settype($row['locationid'], 'int'); settype($row['parentlocationid'], 'int'); $rooms[$row['locationid']] = $row; } foreach ($rooms as &$room) { if ($room['parentlocationid'] > 0 && isset($rooms[$room['parentlocationid']])) { $rooms[$room['parentlocationid']]['skip'] = true; // Don't just unset, might be wrong order } } unset($room); // refd! /* collect names and build room blocks - filter empty rooms while at it */ $roomNames = array(); $roomBlocks = ''; foreach ($rooms as $room) { if (is_null($room['notnull']) || isset($room['skip']) // Not leaf || empty($room['managerip'])) // rooms without managerips don't make sense continue; $roomBlock = PvsGenerator::generateRoomBlock($room); if ($roomBlock === false) continue; // Room nonexistent or empty $roomNames[] = md5($room['locationname']); $roomBlocks .= $roomBlock; } /* output room plus [General]-block */ return "[General]\n" . 'rooms=' . implode(', ', $roomNames) . "\n" . "allowClientQuit=False\n" // TODO: configurable . "showLockDesktopButton=True\n" // TODO: Make this configurable (or not) . "\n\n" . $roomBlocks; } /** * Generate .ini section for specific room. * * @param $room array room/location data as fetched from db * @return string|bool .ini section for room, or false if room is empty */ private static function generateRoomBlock($room) { $out = '[' . md5($room['locationname']) . "]\n"; /* find all clients in that room */ $machines = PvsGenerator::getMachines($room['locationid']); if (empty($machines)) return false; $out .= "name=" . $room['locationname'] . "\n"; /* manager */ $mgr = $room['managerip']; $tutor = $room['tutorip']; if ($mgr) { $out .= 'mgrIP=' . $mgr . "\n"; } /* tutor */ if ($tutor) { $out .= 'tutorIP=' . $tutor . "\n"; } /* grid */ $out .= PvsGenerator::generateGrid($machines); return $out . "\n"; } /** * Generate grid size information and client position data for given clients. * * @param $machines array list of clients * @return string grid and position data as required for a room's .ini section */ private static function generateGrid($machines) { $out = ""; /* this is a virtual grid, we first need this to do some optimizations */ $grid = array(); /* add each contained client with position and ip */ foreach ($machines as $machine) { $grid[$machine['clientip']] = [$machine['gridCol'], $machine['gridRow']]; } /* find bounding box */ PvsGenerator::boundingBox($grid, $minX, $minY, $maxX, $maxY); $clientSizeX = 4; /* TODO: optimize */ $clientSizeY = 4; /* TODO: optimize */ $sizeX = max($maxX - $minX + $clientSizeX, 1); /* never negative */ $sizeY = max($maxY - $minY + $clientSizeY, 1); /* and != 0 to avoid divide-by-zero in pvsmgr */ /* zoom all clients into bounding box */ foreach ($grid as $ip => $pos) { $newX = $grid[$ip][X] - $minX; $newY = $grid[$ip][Y] - $minY; $grid[$ip] = [$newX, $newY]; } $out .= "gridSize=@Size($sizeX $sizeY)\n"; $out .= "clientSize=@Size($clientSizeX $clientSizeY)\n"; $out .= "client\\size=" . count($grid) . "\n"; $i = 1; foreach ($grid as $ip => $pos) { $out .= "client\\" . $i . "\\ip=$ip\n"; $out .= "client\\" . $i++ . "\\pos=@Point(" . $pos[X] . ' ' . $pos[Y] . ")\n"; } return $out; } /** * Get all clients for given room with IP and position. * * @param $roomid int locationid of room * @return array */ private static function getMachines($roomid) { $ret = Database::simpleQuery( 'SELECT clientip, position FROM machine WHERE fixedlocationid = :locationid', ['locationid' => $roomid]); $machines = array(); while ($row = $ret->fetch(PDO::FETCH_ASSOC)) { $position = json_decode($row['position'], true); if ($position === false || !isset($position['gridRow']) || !isset($position['gridCol'])) continue; // TODO: Remove entry/set to NULL? $machine = array(); $machine['clientip'] = $row['clientip']; $machine['gridRow'] = $position['gridRow']; $machine['gridCol'] = $position['gridCol']; $machine['tutor'] = false; /* TODO: find out if machine is default tutor */ $machine['manager'] = false; /* TODO: find out if machine is manager */ $machines[] = $machine; } return $machines; } private static function boundingBox($grid, &$minX, &$minY, &$maxX, &$maxY) { $minX = PHP_INT_MAX; /* PHP_INT_MIN is only avaiable since PHP 7 */ $maxX = ~PHP_INT_MAX; $minY = PHP_INT_MAX; $maxY = ~PHP_INT_MAX; foreach ($grid as $pos) { $minX = min($minX, $pos[X]); $maxX = max($maxX, $pos[X]); $minY = min($minY, $pos[Y]); $maxY = max($maxY, $pos[Y]); } } }