서버 커스터마이징 가능

This commit is contained in:
2020-06-21 17:03:32 +09:00
parent 929d2db5a5
commit 0ec99d8ba1
16 changed files with 2815 additions and 2792 deletions
+313 -305
View File
@@ -1,305 +1,313 @@
<?php
namespace sammo;
require(__DIR__ . '/../vendor/autoload.php');
$host = Util::getPost('db_host');
$port = Util::getPost('db_port', 'int');
$username = Util::getPost('db_id');
$password = Util::getPost('db_pw');
$dbName = Util::getPost('db_name');
$servHost = Util::getPost('serv_host');
$sharedIconPath = Util::getPost('shared_icon_path');
$gameImagePath = Util::getPost('game_image_path');
$imageRequestKey = Util::getPost('image_request_key');
$kakaoRESTKey = Util::getPost('kakao_rest_key', 'string', '');
$kakaoAdminKey = Util::getPost('kakao_admin_key', 'string', '');
if (!$host || !$port || !$username || !$password || !$dbName || !$servHost || !$sharedIconPath || !$gameImagePath) {
Json::die([
'result' => false,
'reason' => '입력 값이 올바르지 않습니다'
]);
}
if (!filter_var($servHost, FILTER_VALIDATE_URL)) {
Json::die([
'result' => false,
'reason' => '접속 경로가 올바르지 않습니다.'
]);
}
if (file_exists(ROOT . '/d_setting/RootDB.php') && is_dir(ROOT . '/d_setting/RootDB.php')) {
Json::die([
'result' => false,
'reason' => 'd_setting/RootDB.php 가 디렉토리입니다'
]);
}
if (class_exists('\\sammo\\RootDB')) {
Json::die([
'result' => false,
'reason' => '이미 RootDB.php 파일이 있습니다'
]);
}
//파일 권한 검사
if (file_exists(AppConf::getUserIconPathFS()) && !is_dir(AppConf::getUserIconPathFS())) {
Json::die([
'result' => false,
'reason' => AppConf::$userIconPath . ' 이 디렉토리가 아닙니다'
]);
}
if (file_exists(ROOT . '/d_log') && !is_dir(ROOT . '/d_log')) {
Json::die([
'result' => false,
'reason' => 'd_log 가 디렉토리가 아닙니다'
]);
}
if (file_exists(ROOT . '/d_shared') && !is_dir(ROOT . '/d_shared')) {
Json::die([
'result' => false,
'reason' => 'd_shared 가 디렉토리가 아닙니다'
]);
}
if (file_exists(ROOT . '/d_setting') && !is_dir(ROOT . '/d_setting')) {
Json::die([
'result' => false,
'reason' => 'd_shared 가 디렉토리가 아닙니다'
]);
}
if (
!file_exists(ROOT . '/d_log')
|| !file_exists(ROOT . '/d_shared')
|| !file_exists(ROOT . '/d_setting')
|| !file_exists(AppConf::getUserIconPathFS())
) {
if (!is_writable(ROOT)) {
Json::die([
'result' => false,
'reason' => '하위 디렉토리 생성 권한이 없습니다'
]);
}
//기본 파일 생성
if (!file_exists(AppConf::getUserIconPathFS())) {
mkdir(AppConf::getUserIconPathFS());
}
if (!file_exists(ROOT . '/d_log')) {
mkdir(ROOT . '/d_log');
}
if (!file_exists(ROOT . '/d_setting')) {
mkdir(ROOT . '/d_setting');
}
if (!file_exists(ROOT . '/d_shared')) {
mkdir(ROOT . '/d_shared');
}
}
if (!is_writable(AppConf::getUserIconPathFS())) {
Json::die([
'result' => false,
'reason' => AppConf::$userIconPath . ' 디렉토리의 쓰기 권한이 없습니다'
]);
}
if (!is_writable(ROOT . '/d_log')) {
Json::die([
'result' => false,
'reason' => 'd_log 디렉토리의 쓰기 권한이 없습니다'
]);
}
if (!is_writable(ROOT . '/d_shared')) {
Json::die([
'result' => false,
'reason' => 'd_shared 디렉토리의 쓰기 권한이 없습니다'
]);
}
if (!is_writable(ROOT . '/d_setting')) {
Json::die([
'result' => false,
'reason' => 'd_setting 디렉토리의 쓰기 권한이 없습니다.'
]);
}
if (!file_exists(ROOT . '/d_log/.htaccess')) {
@file_put_contents(ROOT . '/d_log/.htaccess', 'Deny from all');
}
if (!file_exists(ROOT . '/d_setting/.htaccess')) {
@file_put_contents(ROOT . '/d_setting/.htaccess', 'Deny from all');
}
//DB 접근 권한 검사
$rootDB = new \MeekroDB($host, $username, $password, $dbName, $port, 'utf8mb4');
$rootDB->connect_options[MYSQLI_OPT_INT_AND_FLOAT_NATIVE] = true;
$rootDB->throw_exception_on_nonsql_error = false;
$rootDB->nonsql_error_handler = function ($params) {
Json::die([
'result' => false,
'reason' => 'DB 접속에 실패했습니다.'
]);
};
$rootDB->error_handler = function ($params) {
Json::die([
'result' => false,
'reason' => 'SQL을 제대로 실행하지 못했습니다. DB상태를 확인해 주세요.'
]);
};
$mysqli_obj = $rootDB->get(); //로그인에 실패할 경우 자동으로 dbConnFail()이 실행됨.
if ($mysqli_obj->multi_query(file_get_contents(__DIR__ . '/sql/common_schema.sql'))) {
while (true) {
if (!$mysqli_obj->more_results()) {
break;
}
if (!$mysqli_obj->next_result()) {
break;
}
}
}
$rootDB->insert('system', array(
'REG' => 'N',
'LOGIN' => 'N',
'CRT_DATE' => TimeUtil::now(),
'MDF_DATE' => TimeUtil::now()
));
$globalSalt = bin2hex(random_bytes(16));
'@phan-var-force string $servHost';
'@phan-var-force string $sharedIconPath';
'@phan-var-force string $gameImagePath';
$sharedIconPath = WebUtil::resolveRelativePath($sharedIconPath, $servHost);
$gameImagePath = WebUtil::resolveRelativePath($gameImagePath, $servHost);
$imageRequestPath = WebUtil::resolveRelativePath($gameImagePath . '/../hook/git_pull.php', $servHost);
$imageKeyInstallPath = WebUtil::resolveRelativePath($gameImagePath . '/../hook/InstallKey.php', $servHost);
$result = Util::generateFileUsingSimpleTemplate(
__DIR__ . '/templates/ServConfig.orig.php',
ROOT . '/d_setting/ServConfig.php',
[
'serverBasePath' => $servHost,
'sharedIconPath' => $sharedIconPath,
'gameImagePath' => $gameImagePath,
'imageRequestPath' => $imageRequestPath,
'imageRequestKey' => $imageRequestKey
],
true
);
if ($imageRequestKey) {
@file_get_contents($imageKeyInstallPath . '?key=' . $imageRequestKey);
}
if ($result !== true) {
Json::die([
'result' => false,
'reason' => $result
]);
}
$result = Util::generateFileUsingSimpleTemplate(
__DIR__ . '/templates/common_path.orig.js',
ROOT . '/d_shared/common_path.js',
[
'serverBasePath' => $servHost,
'sharedIconPath' => $sharedIconPath,
'gameImagePath' => $gameImagePath
],
true
);
$result = Util::generateFileUsingSimpleTemplate(
__DIR__ . '/templates/menu.orig.json',
ROOT . '/d_shared/menu.json',
[],
true
);
if ($result !== true) {
Json::die([
'result' => false,
'reason' => $result
]);
}
$result = Util::generateFileUsingSimpleTemplate(
__DIR__ . '/templates/common.orig.css',
ROOT . '/d_shared/common.css',
[
'serverBasePath' => $servHost,
'sharedIconPath' => $sharedIconPath,
'gameImagePath' => $gameImagePath
],
true
);
if ($result !== true) {
Json::die([
'result' => false,
'reason' => $result
]);
}
$result = Util::generateFileUsingSimpleTemplate(
__DIR__ . '/templates/RootDB.orig.php',
ROOT . '/d_setting/RootDB.php',
[
'host' => $host,
'user' => $username,
'password' => $password,
'dbName' => $dbName,
'port' => $port,
'globalSalt' => $globalSalt,
]
);
$kakaoRedirectURI = WebUtil::resolveRelativePath('oauth_kakao/oauth.php', $servHost . '/');
Util::generateFileUsingSimpleTemplate(
__DIR__ . '/templates/KakaoKey.orig.php',
ROOT . '/d_setting/KakaoKey.php',
[
'REST_API_KEY' => $kakaoRESTKey,
'ADMIN_KEY' => $kakaoAdminKey,
'REDIRECT_URI' => $kakaoRedirectURI
],
true
);
if ($result !== true) {
Json::die([
'result' => false,
'reason' => $result
]);
}
Json::die([
'result' => true,
'reason' => 'success',
'globalSalt' => $globalSalt
]);
<?php
namespace sammo;
require(__DIR__ . '/../vendor/autoload.php');
$host = Util::getPost('db_host');
$port = Util::getPost('db_port', 'int');
$username = Util::getPost('db_id');
$password = Util::getPost('db_pw');
$dbName = Util::getPost('db_name');
$servHost = Util::getPost('serv_host');
$sharedIconPath = Util::getPost('shared_icon_path');
$gameImagePath = Util::getPost('game_image_path');
$imageRequestKey = Util::getPost('image_request_key');
$kakaoRESTKey = Util::getPost('kakao_rest_key', 'string', '');
$kakaoAdminKey = Util::getPost('kakao_admin_key', 'string', '');
if (!$host || !$port || !$username || !$password || !$dbName || !$servHost || !$sharedIconPath || !$gameImagePath) {
Json::die([
'result' => false,
'reason' => '입력 값이 올바르지 않습니다'
]);
}
if (!filter_var($servHost, FILTER_VALIDATE_URL)) {
Json::die([
'result' => false,
'reason' => '접속 경로가 올바르지 않습니다.'
]);
}
if (file_exists(ROOT . '/d_setting/RootDB.php') && is_dir(ROOT . '/d_setting/RootDB.php')) {
Json::die([
'result' => false,
'reason' => 'd_setting/RootDB.php 가 디렉토리입니다'
]);
}
if (class_exists('\\sammo\\RootDB')) {
Json::die([
'result' => false,
'reason' => '이미 RootDB.php 파일이 있습니다'
]);
}
//파일 권한 검사
if (file_exists(AppConf::getUserIconPathFS()) && !is_dir(AppConf::getUserIconPathFS())) {
Json::die([
'result' => false,
'reason' => AppConf::$userIconPath . ' 이 디렉토리가 아닙니다'
]);
}
if (file_exists(ROOT . '/d_log') && !is_dir(ROOT . '/d_log')) {
Json::die([
'result' => false,
'reason' => 'd_log 가 디렉토리가 아닙니다'
]);
}
if (file_exists(ROOT . '/d_shared') && !is_dir(ROOT . '/d_shared')) {
Json::die([
'result' => false,
'reason' => 'd_shared 가 디렉토리가 아닙니다'
]);
}
if (file_exists(ROOT . '/d_setting') && !is_dir(ROOT . '/d_setting')) {
Json::die([
'result' => false,
'reason' => 'd_shared 가 디렉토리가 아닙니다'
]);
}
if (
!file_exists(ROOT . '/d_log')
|| !file_exists(ROOT . '/d_shared')
|| !file_exists(ROOT . '/d_setting')
|| !file_exists(AppConf::getUserIconPathFS())
) {
if (!is_writable(ROOT)) {
Json::die([
'result' => false,
'reason' => '하위 디렉토리 생성 권한이 없습니다'
]);
}
//기본 파일 생성
if (!file_exists(AppConf::getUserIconPathFS())) {
mkdir(AppConf::getUserIconPathFS());
}
if (!file_exists(ROOT . '/d_log')) {
mkdir(ROOT . '/d_log');
}
if (!file_exists(ROOT . '/d_setting')) {
mkdir(ROOT . '/d_setting');
}
if (!file_exists(ROOT . '/d_shared')) {
mkdir(ROOT . '/d_shared');
}
}
if (!is_writable(AppConf::getUserIconPathFS())) {
Json::die([
'result' => false,
'reason' => AppConf::$userIconPath . ' 디렉토리의 쓰기 권한이 없습니다'
]);
}
if (!is_writable(ROOT . '/d_log')) {
Json::die([
'result' => false,
'reason' => 'd_log 디렉토리의 쓰기 권한이 없습니다'
]);
}
if (!is_writable(ROOT . '/d_shared')) {
Json::die([
'result' => false,
'reason' => 'd_shared 디렉토리의 쓰기 권한이 없습니다'
]);
}
if (!is_writable(ROOT . '/d_setting')) {
Json::die([
'result' => false,
'reason' => 'd_setting 디렉토리의 쓰기 권한이 없습니다.'
]);
}
if (!file_exists(ROOT . '/d_log/.htaccess')) {
@file_put_contents(ROOT . '/d_log/.htaccess', 'Deny from all');
}
if (!file_exists(ROOT . '/d_setting/.htaccess')) {
@file_put_contents(ROOT . '/d_setting/.htaccess', 'Deny from all');
}
//DB 접근 권한 검사
$rootDB = new \MeekroDB($host, $username, $password, $dbName, $port, 'utf8mb4');
$rootDB->connect_options[MYSQLI_OPT_INT_AND_FLOAT_NATIVE] = true;
$rootDB->throw_exception_on_nonsql_error = false;
$rootDB->nonsql_error_handler = function ($params) {
Json::die([
'result' => false,
'reason' => 'DB 접속에 실패했습니다.'
]);
};
$rootDB->error_handler = function ($params) {
Json::die([
'result' => false,
'reason' => 'SQL을 제대로 실행하지 못했습니다. DB상태를 확인해 주세요.'
]);
};
$mysqli_obj = $rootDB->get(); //로그인에 실패할 경우 자동으로 dbConnFail()이 실행됨.
if ($mysqli_obj->multi_query(file_get_contents(__DIR__ . '/sql/common_schema.sql'))) {
while (true) {
if (!$mysqli_obj->more_results()) {
break;
}
if (!$mysqli_obj->next_result()) {
break;
}
}
}
$rootDB->insert('system', array(
'REG' => 'N',
'LOGIN' => 'N',
'CRT_DATE' => TimeUtil::now(),
'MDF_DATE' => TimeUtil::now()
));
$globalSalt = bin2hex(random_bytes(16));
'@phan-var-force string $servHost';
'@phan-var-force string $sharedIconPath';
'@phan-var-force string $gameImagePath';
$sharedIconPath = WebUtil::resolveRelativePath($sharedIconPath, $servHost);
$gameImagePath = WebUtil::resolveRelativePath($gameImagePath, $servHost);
$imageRequestPath = WebUtil::resolveRelativePath($gameImagePath . '/../hook/git_pull.php', $servHost);
$imageKeyInstallPath = WebUtil::resolveRelativePath($gameImagePath . '/../hook/InstallKey.php', $servHost);
$result = Util::generateFileUsingSimpleTemplate(
__DIR__ . '/templates/ServConfig.orig.php',
ROOT . '/d_setting/ServConfig.php',
[
'serverBasePath' => $servHost,
'sharedIconPath' => $sharedIconPath,
'gameImagePath' => $gameImagePath,
'imageRequestPath' => $imageRequestPath,
'imageRequestKey' => $imageRequestKey,
'serverList' => [
['che', '체', 'white'],
['kwe', '퀘', 'yellow'],
['pwe', '풰', 'orange'],
['twe', '퉤', 'magenta'],
['nya', '냐', '#e67e22'],
['pya', '퍄', '#9b59b6']
]
],
true
);
if ($imageRequestKey) {
@file_get_contents($imageKeyInstallPath . '?key=' . $imageRequestKey);
}
if ($result !== true) {
Json::die([
'result' => false,
'reason' => $result
]);
}
$result = Util::generateFileUsingSimpleTemplate(
__DIR__ . '/templates/common_path.orig.js',
ROOT . '/d_shared/common_path.js',
[
'serverBasePath' => $servHost,
'sharedIconPath' => $sharedIconPath,
'gameImagePath' => $gameImagePath
],
true
);
$result = Util::generateFileUsingSimpleTemplate(
__DIR__ . '/templates/menu.orig.json',
ROOT . '/d_shared/menu.json',
[],
true
);
if ($result !== true) {
Json::die([
'result' => false,
'reason' => $result
]);
}
$result = Util::generateFileUsingSimpleTemplate(
__DIR__ . '/templates/common.orig.css',
ROOT . '/d_shared/common.css',
[
'serverBasePath' => $servHost,
'sharedIconPath' => $sharedIconPath,
'gameImagePath' => $gameImagePath
],
true
);
if ($result !== true) {
Json::die([
'result' => false,
'reason' => $result
]);
}
$result = Util::generateFileUsingSimpleTemplate(
__DIR__ . '/templates/RootDB.orig.php',
ROOT . '/d_setting/RootDB.php',
[
'host' => $host,
'user' => $username,
'password' => $password,
'dbName' => $dbName,
'port' => $port,
'globalSalt' => $globalSalt,
]
);
$kakaoRedirectURI = WebUtil::resolveRelativePath('oauth_kakao/oauth.php', $servHost . '/');
Util::generateFileUsingSimpleTemplate(
__DIR__ . '/templates/KakaoKey.orig.php',
ROOT . '/d_setting/KakaoKey.php',
[
'REST_API_KEY' => $kakaoRESTKey,
'ADMIN_KEY' => $kakaoAdminKey,
'REDIRECT_URI' => $kakaoRedirectURI
],
true
);
if ($result !== true) {
Json::die([
'result' => false,
'reason' => $result
]);
}
Json::die([
'result' => true,
'reason' => 'success',
'globalSalt' => $globalSalt
]);
+74 -55
View File
@@ -1,55 +1,74 @@
<?php
namespace sammo;
class ServConfig
{
private function __construct()
{
}
public static $serverWebPath = '_tK_serverBasePath_';
public static $sharedIconPath = '_tK_sharedIconPath_';
public static $gameImagePath = "_tK_gameImagePath_";
public static $imageRequestPath = "_tK_imageRequestPath_";
public static $imageRequestKey = '_tK_imageRequestKey_';
public static function getSharedIconPath(string $filepath = ''): string
{
if ($filepath) {
return static::$sharedIconPath . "/{$filepath}";
}
return static::$sharedIconPath;
}
public static function getUserIconPath(string $filepath = ''): string
{
return AppConf::getUserIconPathWeb($filepath);
}
public static function getGameImagePath(string $filepath = ''): string
{
if ($filepath) {
return static::$gameImagePath . "/{$filepath}";
}
return static::$gameImagePath;
}
public static function getImagePullURI(): string
{
$now = time();
$req_hash = Util::hashPassword(sprintf("%016x", $now), static::$imageRequestKey);
return static::$imageRequestPath . "?req={$req_hash}&time={$now}";
}
/**
* 서버 주소 반환. 서버의 경로가 하부 디렉토리인 경우에 하부 디렉토리까지 포함
*
* @return string
*/
public static function getServerBasepath(): string
{
return self::$serverWebPath;
}
}
<?php
namespace sammo;
class ServConfig
{
private function __construct()
{
}
public static $serverWebPath = '_tK_serverBasePath_';
public static $sharedIconPath = '_tK_sharedIconPath_';
public static $gameImagePath = "_tK_gameImagePath_";
public static $imageRequestPath = "_tK_imageRequestPath_";
public static $imageRequestKey = '_tK_imageRequestKey_';
private static $serverList = null;
public static function getSharedIconPath(string $filepath = ''): string
{
if ($filepath) {
return static::$sharedIconPath . "/{$filepath}";
}
return static::$sharedIconPath;
}
public static function getUserIconPath(string $filepath = ''): string
{
return AppConf::getUserIconPathWeb($filepath);
}
public static function getGameImagePath(string $filepath = ''): string
{
if ($filepath) {
return static::$gameImagePath . "/{$filepath}";
}
return static::$gameImagePath;
}
public static function getImagePullURI(): string
{
$now = time();
$req_hash = Util::hashPassword(sprintf("%016x", $now), static::$imageRequestKey);
return static::$imageRequestPath . "?req={$req_hash}&time={$now}";
}
/**
* 서버 설정 반환
*
* @return \sammo\Setting[]
*/
public static function getServerList(): array{
$servKeyList = [/*_tK_serverList_*/];
$servKeyList[] = ['hwe', '훼', 'red'];
if (self::$serverList === null) {
self::$serverList = [];
foreach($servKeyList as [$servKey, $servNick, $servColor]){
self::$serverList[] = new Setting(ROOT.'/'.$servKey, $servNick, $servColor);
}
}
return self::$serverList;
}
/**
* 서버 주소 반환. 서버의 경로가 하부 디렉토리인 경우에 하부 디렉토리까지 포함
*
* @return string
*/
public static function getServerBasepath(): string
{
return self::$serverWebPath;
}
}
+100 -100
View File
@@ -1,101 +1,101 @@
<?php
namespace sammo;
include 'lib.php';
include "func.php";
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
if (!$db->queryFirstField("SHOW TABLES LIKE 'reserved_open'")) {
Json::die([
'result'=>true,
'affected'=>0,
'status'=>'no_reserved_table'
]);
}
$reserved = $db->queryFirstRow('SELECT `date`, options FROM reserved_open ORDER BY `date` ASC LIMIT 1');
if(!$reserved){
Json::die([
'result'=>true,
'affected'=>0,
'status'=>'no_reserved'
]);
}
$reservedDate = new \DateTime($reserved['date']);
$now = new \DateTime();
$status = 'not_yet';
list($isUnited, $lastTurn) = $gameStor->getValuesAsArray(['isunited', 'turntime']);
if($isUnited === null || $lastTurn === null){
$isUnited = 2;
$lastTurn = '2000-01-01';
}
if($lastTurn !== null){
$lastTurn = new \DateTime($lastTurn);
}
if($lastTurn === null){
//이미 리셋된 상태임
}
else if(file_exists(__DIR__.'/.htaccess')){
//일단 서버는 닫혀 있음
}
else if(
$isUnited == 2 &&
$now->getTimestamp() - $lastTurn->getTimestamp() > $reservedDate->getTimestamp() - $now->getTimestamp()
){
//정지 상태 & 중간 넘음
AppConf::getList()[DB::prefix()]->closeServer();
$status = 'closed';
}
else if(
$isUnited > 0 &&
$now->getTimestamp() - $lastTurn->getTimestamp() > ($reservedDate->getTimestamp() - $now->getTimestamp()) * 2
){
//천통 & 비정지 상태 & 2/3 넘음
AppConf::getList()[DB::prefix()]->closeServer();
$status = 'closed';
}
else if($reservedDate->getTimestamp() - $now->getTimestamp() <= 60*10){
//어쨌든 간에 10분 남았다면.
AppConf::getList()[DB::prefix()]->closeServer();
$status = 'closed';
}
if($now < $reservedDate){
Json::die([
'result'=>true,
'affected'=>0,
'status'=>$status
]);
}
$options = Json::decode($reserved['options']);
$result = ResetHelper::buildScenario(
$options['turnterm'],
$options['sync'],
$options['scenario'],
$options['fiction'],
$options['extend'],
$options['npcmode'],
$options['show_img_level'],
$options['tournament_trig'],
$options['join_mode'],
$options['starttime'],
$options['autorun_user']?:null
);
$result['affected']=1;
$prefix = DB::prefix();
AppConf::getList()[$prefix]->openServer();
<?php
namespace sammo;
include 'lib.php';
include "func.php";
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
if (!$db->queryFirstField("SHOW TABLES LIKE 'reserved_open'")) {
Json::die([
'result'=>true,
'affected'=>0,
'status'=>'no_reserved_table'
]);
}
$reserved = $db->queryFirstRow('SELECT `date`, options FROM reserved_open ORDER BY `date` ASC LIMIT 1');
if(!$reserved){
Json::die([
'result'=>true,
'affected'=>0,
'status'=>'no_reserved'
]);
}
$reservedDate = new \DateTime($reserved['date']);
$now = new \DateTime();
$status = 'not_yet';
list($isUnited, $lastTurn) = $gameStor->getValuesAsArray(['isunited', 'turntime']);
if($isUnited === null || $lastTurn === null){
$isUnited = 2;
$lastTurn = '2000-01-01';
}
if($lastTurn !== null){
$lastTurn = new \DateTime($lastTurn);
}
if($lastTurn === null){
//이미 리셋된 상태임
}
else if(file_exists(__DIR__.'/.htaccess')){
//일단 서버는 닫혀 있음
}
else if(
$isUnited == 2 &&
$now->getTimestamp() - $lastTurn->getTimestamp() > $reservedDate->getTimestamp() - $now->getTimestamp()
){
//정지 상태 & 중간 넘음
ServConfig::getServerList()[DB::prefix()]->closeServer();
$status = 'closed';
}
else if(
$isUnited > 0 &&
$now->getTimestamp() - $lastTurn->getTimestamp() > ($reservedDate->getTimestamp() - $now->getTimestamp()) * 2
){
//천통 & 비정지 상태 & 2/3 넘음
ServConfig::getServerList()[DB::prefix()]->closeServer();
$status = 'closed';
}
else if($reservedDate->getTimestamp() - $now->getTimestamp() <= 60*10){
//어쨌든 간에 10분 남았다면.
ServConfig::getServerList()[DB::prefix()]->closeServer();
$status = 'closed';
}
if($now < $reservedDate){
Json::die([
'result'=>true,
'affected'=>0,
'status'=>$status
]);
}
$options = Json::decode($reserved['options']);
$result = ResetHelper::buildScenario(
$options['turnterm'],
$options['sync'],
$options['scenario'],
$options['fiction'],
$options['extend'],
$options['npcmode'],
$options['show_img_level'],
$options['tournament_trig'],
$options['join_mode'],
$options['starttime'],
$options['autorun_user']?:null
);
$result['affected']=1;
$prefix = DB::prefix();
ServConfig::getServerList()[$prefix]->openServer();
Json::die($result);
+208 -208
View File
@@ -1,209 +1,209 @@
<?php
namespace sammo;
include "lib.php";
include "func.php";
WebUtil::requireAJAX();
$session = Session::requireLogin([])->setReadOnly();
if(!class_exists('\\sammo\\DB')){
Json::die([
'result'=>false,
'reason'=>'DB리셋 필요'
]);
}
$serverName = DB::prefix();
$serverAcl = $session->acl[$serverName]??[];
$allowReset = in_array('reset', $serverAcl);
$allowFullReset = in_array('fullReset',$serverAcl);
$allowReset |= $allowFullReset;
$reserve_open = Util::getPost('reserve_open');
if($reserve_open && $reserve_open < date('Y-m-d H:i')){
Json::die([
'result'=>false,
'reason'=>'현재 시간보다 이전 시간대를 예약 시간으로 지정했습니다.'
]);
}
$pre_reserve_open = Util::getPost('pre_reserve_open');
if($pre_reserve_open && !$reserve_open){
Json::die([
'result'=>false,
'reason'=>'가오픈 예약을 위해선 오픈 예약을 지정해야합니다.'
]);
}
if($pre_reserve_open && $pre_reserve_open >= $reserve_open){
Json::die([
'result'=>false,
'reason'=>'가오픈 시간이 오픈 예약 시점보다 이전이어야 합니다.'
]);
}
if($session->userGrade < 5 && !$allowReset){
Json::die([
'result'=>false,
'reason'=>'관리자 아님'
]);
}
$v = new Validator($_POST);
$v->rule('required', [
'turnterm',
'sync',
'scenario',
'fiction',
'extend',
'join_mode',
'npcmode',
'show_img_level',
'autorun_user_minutes'
])->rule('integer', [
'turnterm',
'sync',
'scenario',
'fiction',
'extend',
'npcmode',
'show_img_level',
'tournament_trig',
'autorun_user_minutes'
])->rule('in', 'join_mode', ['onlyRandom', 'full']);
if(!$v->validate()){
Json::die([
'result'=>false,
'reason'=>$v->errorStr()
]);
}
$allowReset = true;
if($session->userGrade < 5 && !$allowFullReset){
//리셋 가능한 조건인지 테스트
$allowReset = false;
if(file_exists(__DIR__.'/.htaccess')){
$allowReset = true;
}
else{
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
if($gameStor->isunited){
$allowReset = true;
}
}
}
if(!$allowReset){
Json::die([
'result'=>false,
'reason'=>'부족한 권한: 서버가 닫혀있거나, 천통되어 있을 경우에만 리셋 가능합니다.'
]);
}
$turnterm = (int)$_POST['turnterm'];
$sync = (int)$_POST['sync'];
$scenario = (int)$_POST['scenario'];
$fiction = (int)$_POST['fiction'];
$extend = (int)$_POST['extend'];
$npcmode = (int)$_POST['npcmode'];
$show_img_level = (int)$_POST['show_img_level'];
$tournament_trig = (int)$_POST['tournament_trig'];
$join_mode = $_POST['join_mode'];
$autorun_user_minutes = (int)$_POST['autorun_user_minutes'];
$autorun_user_options = [];
foreach(Util::getPost('autorun_user', 'array_string', []) as $autorun_option){
$autorun_user_options[$autorun_option] = 1;
}
if($autorun_user_minutes > 0 && !$autorun_user_options){
Json::die([
'result'=>false,
'reason'=>'적어도 자동 행동 중 하나는 선택을 해야합니다.'
]);
}
if($autorun_user_minutes < 0){
Json::die([
'result'=>false,
'reason'=>'자동 행동 기한이 0보다 작을 수 없습니다.'
]);
}
$autorun_user = $autorun_user_minutes?[
'limit_minutes'=>$autorun_user_minutes,
'options'=>$autorun_user_options
]:null;
if($reserve_open){
$reserve_open = new \DateTime($reserve_open);
$db = DB::db();
if (!$db->queryFirstField("SHOW TABLES LIKE 'storage'")) {
$clearResult = ResetHelper::clearDB();
if(!$clearResult['result']){
Json::die($clearResult);
}
}
if (!$db->queryFirstField("SHOW TABLES LIKE 'reserved_open'")) {
Json::die([
'result'=>false,
'reason'=>'예약 테이블이 없음!'
]);
}
$scenarioObj = new Scenario($scenario, true);
$open_date = $reserve_open->format('Y-m-d H:i:s');
$reserveInfo = [
'turnterm'=>$turnterm,
'sync'=>$sync,
'scenario'=>$scenario,
'scenarioName'=>$scenarioObj->getTitle(),
'fiction'=>$fiction,
'extend'=>$extend,
'npcmode'=>$npcmode,
'show_img_level'=>$show_img_level,
'tournament_trig'=>$tournament_trig,
'gameConf'=>$scenarioObj->getGameConf(),
'join_mode'=>$join_mode,
'starttime'=>$open_date,
'autorun_user'=>$autorun_user
];
if($pre_reserve_open){
$pre_reserve_open = new \DateTime($pre_reserve_open);
$open_date = $pre_reserve_open->format('Y-m-d H:i:s');
}
$db->delete('reserved_open', true);
$db->insert('reserved_open', [
'options'=>Json::encode($reserveInfo),
'date'=>$open_date
]);
AppConf::getList()[DB::prefix()]->closeServer();
Json::die([
'result'=>true,
'reason'=>'예약'
]);
}
Json::die(ResetHelper::buildScenario(
$turnterm,
$sync,
$scenario,
$fiction,
$extend,
$npcmode,
$show_img_level,
$tournament_trig,
$join_mode,
TimeUtil::now(),
$autorun_user
<?php
namespace sammo;
include "lib.php";
include "func.php";
WebUtil::requireAJAX();
$session = Session::requireLogin([])->setReadOnly();
if(!class_exists('\\sammo\\DB')){
Json::die([
'result'=>false,
'reason'=>'DB리셋 필요'
]);
}
$serverName = DB::prefix();
$serverAcl = $session->acl[$serverName]??[];
$allowReset = in_array('reset', $serverAcl);
$allowFullReset = in_array('fullReset',$serverAcl);
$allowReset |= $allowFullReset;
$reserve_open = Util::getPost('reserve_open');
if($reserve_open && $reserve_open < date('Y-m-d H:i')){
Json::die([
'result'=>false,
'reason'=>'현재 시간보다 이전 시간대를 예약 시간으로 지정했습니다.'
]);
}
$pre_reserve_open = Util::getPost('pre_reserve_open');
if($pre_reserve_open && !$reserve_open){
Json::die([
'result'=>false,
'reason'=>'가오픈 예약을 위해선 오픈 예약을 지정해야합니다.'
]);
}
if($pre_reserve_open && $pre_reserve_open >= $reserve_open){
Json::die([
'result'=>false,
'reason'=>'가오픈 시간이 오픈 예약 시점보다 이전이어야 합니다.'
]);
}
if($session->userGrade < 5 && !$allowReset){
Json::die([
'result'=>false,
'reason'=>'관리자 아님'
]);
}
$v = new Validator($_POST);
$v->rule('required', [
'turnterm',
'sync',
'scenario',
'fiction',
'extend',
'join_mode',
'npcmode',
'show_img_level',
'autorun_user_minutes'
])->rule('integer', [
'turnterm',
'sync',
'scenario',
'fiction',
'extend',
'npcmode',
'show_img_level',
'tournament_trig',
'autorun_user_minutes'
])->rule('in', 'join_mode', ['onlyRandom', 'full']);
if(!$v->validate()){
Json::die([
'result'=>false,
'reason'=>$v->errorStr()
]);
}
$allowReset = true;
if($session->userGrade < 5 && !$allowFullReset){
//리셋 가능한 조건인지 테스트
$allowReset = false;
if(file_exists(__DIR__.'/.htaccess')){
$allowReset = true;
}
else{
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
if($gameStor->isunited){
$allowReset = true;
}
}
}
if(!$allowReset){
Json::die([
'result'=>false,
'reason'=>'부족한 권한: 서버가 닫혀있거나, 천통되어 있을 경우에만 리셋 가능합니다.'
]);
}
$turnterm = (int)$_POST['turnterm'];
$sync = (int)$_POST['sync'];
$scenario = (int)$_POST['scenario'];
$fiction = (int)$_POST['fiction'];
$extend = (int)$_POST['extend'];
$npcmode = (int)$_POST['npcmode'];
$show_img_level = (int)$_POST['show_img_level'];
$tournament_trig = (int)$_POST['tournament_trig'];
$join_mode = $_POST['join_mode'];
$autorun_user_minutes = (int)$_POST['autorun_user_minutes'];
$autorun_user_options = [];
foreach(Util::getPost('autorun_user', 'array_string', []) as $autorun_option){
$autorun_user_options[$autorun_option] = 1;
}
if($autorun_user_minutes > 0 && !$autorun_user_options){
Json::die([
'result'=>false,
'reason'=>'적어도 자동 행동 중 하나는 선택을 해야합니다.'
]);
}
if($autorun_user_minutes < 0){
Json::die([
'result'=>false,
'reason'=>'자동 행동 기한이 0보다 작을 수 없습니다.'
]);
}
$autorun_user = $autorun_user_minutes?[
'limit_minutes'=>$autorun_user_minutes,
'options'=>$autorun_user_options
]:null;
if($reserve_open){
$reserve_open = new \DateTime($reserve_open);
$db = DB::db();
if (!$db->queryFirstField("SHOW TABLES LIKE 'storage'")) {
$clearResult = ResetHelper::clearDB();
if(!$clearResult['result']){
Json::die($clearResult);
}
}
if (!$db->queryFirstField("SHOW TABLES LIKE 'reserved_open'")) {
Json::die([
'result'=>false,
'reason'=>'예약 테이블이 없음!'
]);
}
$scenarioObj = new Scenario($scenario, true);
$open_date = $reserve_open->format('Y-m-d H:i:s');
$reserveInfo = [
'turnterm'=>$turnterm,
'sync'=>$sync,
'scenario'=>$scenario,
'scenarioName'=>$scenarioObj->getTitle(),
'fiction'=>$fiction,
'extend'=>$extend,
'npcmode'=>$npcmode,
'show_img_level'=>$show_img_level,
'tournament_trig'=>$tournament_trig,
'gameConf'=>$scenarioObj->getGameConf(),
'join_mode'=>$join_mode,
'starttime'=>$open_date,
'autorun_user'=>$autorun_user
];
if($pre_reserve_open){
$pre_reserve_open = new \DateTime($pre_reserve_open);
$open_date = $pre_reserve_open->format('Y-m-d H:i:s');
}
$db->delete('reserved_open', true);
$db->insert('reserved_open', [
'options'=>Json::encode($reserveInfo),
'date'=>$open_date
]);
ServConfig::getServerList()[DB::prefix()]->closeServer();
Json::die([
'result'=>true,
'reason'=>'예약'
]);
}
Json::die(ResetHelper::buildScenario(
$turnterm,
$sync,
$scenario,
$fiction,
$extend,
$npcmode,
$show_img_level,
$tournament_trig,
$join_mode,
TimeUtil::now(),
$autorun_user
));
+102 -102
View File
@@ -1,103 +1,103 @@
<?php
namespace sammo;
include "lib.php";
include "func.php";
WebUtil::requireAJAX();
$session = Session::requireLogin([])->setReadOnly();
if($session->userGrade < 6){
Json::die([
'result'=>false,
'reason'=>'관리자 아님'
]);
}
$fullReset = Util::getPost('full_reset', 'bool', false);
$host = Util::getPost('db_host');
$port = Util::getPost('db_port', 'int');
$username = Util::getPost('db_id');
$password = Util::getPost('db_pw');
$dbName = Util::getPost('db_name');
if(!$host || !$port || !$username || !$password || !$dbName){
Json::die([
'result'=>false,
'reason'=>'입력 값이 올바르지 않습니다'
]);
}
if($fullReset && class_exists('\\sammo\\DB')){
$mysqli_obj = DB::db()->get();
if($mysqli_obj->multi_query(file_get_contents(__DIR__.'/sql/reset.sql'))){
while(true){
if (!$mysqli_obj->more_results()) {
break;
}
if(!$mysqli_obj->next_result()){
break;
}
}
}
}
if($fullReset){
FileUtil::delInDir(__DIR__."/logs");
FileUtil::delInDir(__DIR__."/data");
if(file_exists(__DIR__.'/d_setting/DB.php')){
@unlink(__DIR__.'/d_setting/DB.php');
}
if(file_exists(__DIR__.'/d_setting/UniqueConst.php')){
@unlink(__DIR__.'/d_setting/UniqueConst.php');
}
}
function dbConnFail($params){
Json::die([
'result'=>false,
'reason'=>'DB 접속에 실패했습니다.'
]);
}
$db = new \MeekroDB($host,$username,$password,$dbName,$port,'utf8mb4');
$db->connect_options[MYSQLI_OPT_INT_AND_FLOAT_NATIVE] = true;
$db->throw_exception_on_nonsql_error = false;
$db->nonsql_error_handler = 'dbConnFail';
$mysqli_obj = $db->get(); //로그인에 실패할 경우 자동으로 dbConnFail()이 실행됨.
$prefix = basename(__DIR__);
$result = Util::generateFileUsingSimpleTemplate(
__DIR__.'/d_setting/DB.orig.php',
__DIR__.'/d_setting/DB.php',[
'host'=>$host,
'user'=>$username,
'password'=>$password,
'dbName'=>$dbName,
'port'=>$port,
'prefix'=>$prefix
], true
);
if($result !== true){
Json::die([
'result'=>false,
'reason'=>$result
]);
}
ResetHelper::clearDB();
AppConf::getList()[$prefix]->closeServer();
Json::die([
'result'=>true,
'reason'=>'success'
<?php
namespace sammo;
include "lib.php";
include "func.php";
WebUtil::requireAJAX();
$session = Session::requireLogin([])->setReadOnly();
if($session->userGrade < 6){
Json::die([
'result'=>false,
'reason'=>'관리자 아님'
]);
}
$fullReset = Util::getPost('full_reset', 'bool', false);
$host = Util::getPost('db_host');
$port = Util::getPost('db_port', 'int');
$username = Util::getPost('db_id');
$password = Util::getPost('db_pw');
$dbName = Util::getPost('db_name');
if(!$host || !$port || !$username || !$password || !$dbName){
Json::die([
'result'=>false,
'reason'=>'입력 값이 올바르지 않습니다'
]);
}
if($fullReset && class_exists('\\sammo\\DB')){
$mysqli_obj = DB::db()->get();
if($mysqli_obj->multi_query(file_get_contents(__DIR__.'/sql/reset.sql'))){
while(true){
if (!$mysqli_obj->more_results()) {
break;
}
if(!$mysqli_obj->next_result()){
break;
}
}
}
}
if($fullReset){
FileUtil::delInDir(__DIR__."/logs");
FileUtil::delInDir(__DIR__."/data");
if(file_exists(__DIR__.'/d_setting/DB.php')){
@unlink(__DIR__.'/d_setting/DB.php');
}
if(file_exists(__DIR__.'/d_setting/UniqueConst.php')){
@unlink(__DIR__.'/d_setting/UniqueConst.php');
}
}
function dbConnFail($params){
Json::die([
'result'=>false,
'reason'=>'DB 접속에 실패했습니다.'
]);
}
$db = new \MeekroDB($host,$username,$password,$dbName,$port,'utf8mb4');
$db->connect_options[MYSQLI_OPT_INT_AND_FLOAT_NATIVE] = true;
$db->throw_exception_on_nonsql_error = false;
$db->nonsql_error_handler = 'dbConnFail';
$mysqli_obj = $db->get(); //로그인에 실패할 경우 자동으로 dbConnFail()이 실행됨.
$prefix = basename(__DIR__);
$result = Util::generateFileUsingSimpleTemplate(
__DIR__.'/d_setting/DB.orig.php',
__DIR__.'/d_setting/DB.php',[
'host'=>$host,
'user'=>$username,
'password'=>$password,
'dbName'=>$dbName,
'port'=>$port,
'prefix'=>$prefix
], true
);
if($result !== true){
Json::die([
'result'=>false,
'reason'=>$result
]);
}
ResetHelper::clearDB();
ServConfig::getServerList()[$prefix]->closeServer();
Json::die([
'result'=>true,
'reason'=>'success'
]);
+357 -357
View File
@@ -1,358 +1,358 @@
<?php
namespace sammo;
class ResetHelper{
private function __construct(){
}
static public function clearDB(){
$servRoot = realpath(__DIR__.'/../');
if(!file_exists($servRoot.'/logs') || !file_exists($servRoot.'/data')){
if(!is_writable($servRoot)){
return [
'result'=>false,
'reason'=>'logs, data 디렉토리를 생성할 권한이 없습니다.'
];
}
mkdir($servRoot.'/logs', 0755);
mkdir($servRoot.'/data', 0755);
}
if(!is_writable($servRoot.'/logs')){
return [
'result'=>false,
'reason'=>'logs 디렉토리의 쓰기 권한이 없습니다'
];
}
if(!is_writable($servRoot.'/data')){
return [
'result'=>false,
'reason'=>'data 디렉토리의 쓰기 권한이 없습니다'
];
}
if(!is_writable($servRoot.'/d_setting')){
return [
'result'=>false,
'reason'=>'d_setting 디렉토리의 쓰기 권한이 없습니다'
];
}
if(!file_exists($servRoot.'/logs/preserved')){
mkdir($servRoot.'/logs/preserved', 0755);
}
if(!file_exists($servRoot.'/logs/.htaccess')){
@file_put_contents($servRoot.'/logs/.htaccess', 'Deny from all');
}
if(!file_exists($servRoot.'/data/.htaccess')){
@file_put_contents($servRoot.'/data/.htaccess', 'Deny from all');
}
$dir = new \DirectoryIterator($servRoot.'/logs');
foreach ($dir as $fileinfo) {
/** @var \DirectoryIterator $fileinfo */
if (!$fileinfo->isDir() || $fileinfo->isDot()) {
continue;
}
$basename = $fileinfo->getFilename();
if($basename == 'preserved'){
continue;
}
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
exec("move ".escapeshellarg($servRoot.'/logs/'.$basename)." ".escapeshellarg($servRoot.'/logs/preserved/'.$basename));
} else {
exec("mv ".escapeshellarg($servRoot.'/logs/'.$basename)." ".escapeshellarg($servRoot.'/logs/preserved/'.$basename));
}
}
$prefix = DB::prefix();
AppConf::getList()[$prefix]->closeServer();
$db = DB::db();
$mysqli_obj = $db->get();
$serverID = DB::prefix().'_'.date("ymd").'_'.Util::randomStr(4);
mkdir($servRoot.'/logs/'.$serverID, 0755);
mkdir($servRoot.'/data/'.$serverID, 0755);
$seasonIdx = 1;
if($db->queryFirstField('SHOW TABLES LIKE %s', 'storage') !== null){
$gameStor = KVStorage::getStorage($db, 'game_env');
$nextSeasonIdx = $gameStor->next_season_idx;
if($nextSeasonIdx !== null){
$seasonIdx = $nextSeasonIdx;
}
$gameStor->resetCache();
}
$result = Util::generateFileUsingSimpleTemplate(
$servRoot.'/d_setting/UniqueConst.orig.php',
$servRoot.'/d_setting/UniqueConst.php',[
'serverID'=>$serverID,
'serverName'=>AppConf::getList()[$prefix]->getKorName(),
'seasonIdx'=>$seasonIdx,
], true
);
copy(
$servRoot.'/d_setting/GameConst.orig.php',
$servRoot.'/d_setting/GameConst.php',
);
if($mysqli_obj->multi_query(file_get_contents($servRoot.'/sql/reset.sql'))){
while(true){
if (!$mysqli_obj->more_results()) {
break;
}
if(!$mysqli_obj->next_result()){
break;
}
}
}
if($mysqli_obj->multi_query(file_get_contents($servRoot.'/sql/schema.sql'))){
while(true){
if (!$mysqli_obj->more_results()) {
break;
}
if(!$mysqli_obj->next_result()){
break;
}
}
}
$gameStor = KVStorage::getStorage($db, 'game_env');
$gameStor->resetValues();
$gameStor->next_season_idx = $seasonIdx;
return [
'result'=>true,
'serverID'=>$serverID,
'seasonIdx'=>$seasonIdx
];
}
static public function buildScenario(
int $turnterm,
int $sync,
int $scenario,
int $fiction,
int $extend,
int $npcmode,
int $show_img_level,
int $tournament_trig,
string $join_mode,
string $turntime,
?array $autorun_user
):array{
//FIXME: 분리할 것
if(120 % $turnterm != 0){
return [
'result'=>false,
'reason'=>'turnterm은 120의 약수여야 합니다.'
];
}
if($tournament_trig < 0 || $tournament_trig > 7){
return [
'result'=>false,
'reason'=>'올바르지 않은 토너먼트 주기입니다.'
];
}
$clearResult = self::clearDB();
if(!$clearResult['result']){
return $clearResult;
}
$serverID = $clearResult['serverID'];
$seasonIdx = $clearResult['seasonIdx'];
$scenarioObj = new Scenario($scenario, false);
$scenarioObj->buildConf();
$startyear = $scenarioObj->getYear()??GameConst::$defaultStartYear;
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$db->insert('plock', [
'plock'=>1,
'locktime'=>TimeUtil::now(true)
]);
$prevWinner = $db->queryFirstField('SELECT l12name FROM emperior ORDER BY `no` DESC LIMIT 1');
$serverCnt = $db->queryFirstField('SELECT count(*) FROM ng_games') + 1;
CityConst::build();
$cityPositions = [];
foreach(CityConst::all() as $city){
$cityPositions[$city->id] = [
$city->name,
$city->posX,
$city->posY
];
}
Util::generateFileUsingSimpleTemplate(
__DIR__.'/../templates/base_map.orig.js',
__DIR__.'/../d_shared/base_map.js',
[
'cityPosition'=>Json::encode($cityPositions),
'regionMap'=>Json::encode(CityConst::$regionMap),
'levelMap'=>Json::encode(CityConst::$levelMap),
],
true
);
if($sync == 0) {
// 현재 시간을 1월로 맞춤
$starttime = cutTurn($turntime, $turnterm);
$month = 1;
$year = $startyear;
} else {
// 현재 시간과 동기화
[$starttime, $yearPulled, $month] = cutDay($turntime, $turnterm);
if($yearPulled){
$year = $startyear-1;
}
else{
$year = $startyear;
}
}
$killturn = 4800 / $turnterm;
if($npcmode == 1) { $killturn = intdiv($killturn, 3); }
$develcost = ($year - $startyear + 10) * 2;
$env = [
'scenario'=>$scenario,
'scenario_text'=>$scenarioObj->getTitle(),
'icon_path'=>$scenarioObj->getIconPath(),
'startyear'=>$startyear,
'year'=> $year,
'month'=> $month,
'init_year'=> $year,
'init_month'=>$month,
'map_theme' => $scenarioObj->getMapTheme(),
'season'=>$seasonIdx,
'msg'=>'공지사항',//TODO:공지사항
'maxgeneral'=>GameConst::$defaultMaxGeneral,
'maxnation'=>GameConst::$defaultMaxNation,
'conlimit'=>30000,
'develcost'=>$develcost,
'turntime'=>$turntime,
'starttime'=>$starttime,
'opentime'=>$turntime,
'turnterm'=>$turnterm,
'killturn'=>$killturn,
'genius'=>GameConst::$defaultMaxGenius,
'show_img_level'=>$show_img_level,
'join_mode'=>$join_mode,
'npcmode'=>$npcmode,
'extended_general'=>$extend,
'fiction'=>$fiction,
'tnmt_trig'=>$tournament_trig,
'prev_winner'=>$prevWinner,
'autorun_user'=>$autorun_user,
'tournament'=>0,
'server_cnt'=>$serverCnt,
];
$db->insert('betting', [
'general_id'=>0,
'bet0'=>0,
'bet1'=>0,
'bet2'=>0,
'bet3'=>0,
'bet4'=>0,
'bet5'=>0,
'bet6'=>0,
'bet7'=>0,
'bet8'=>0,
'bet9'=>0,
'bet10'=>0,
'bet11'=>0,
'bet12'=>0,
'bet13'=>0,
'bet14'=>0,
'bet15'=>0,
]);
foreach(RootDB::db()->query('SELECT `no`, `name`, `picture`, `imgsvr` FROM member WHERE grade >= 5') as $admin){
$db->insert('general', [
'owner'=>$admin['no'],
'name'=>$admin['name'],
'picture'=>$admin['picture'],
'imgsvr'=>$admin['imgsvr'],
'turntime'=>$turntime,
'killturn'=>9999,
'crewtype'=>GameUnitConst::DEFAULT_CREWTYPE
]);
$generalID = $db->insertId();
$turnRows = [];
foreach(Util::range(GameConst::$maxTurn) as $turnIdx){
$turnRows[] = [
'general_id'=>$generalID,
'turn_idx'=>$turnIdx,
'action'=>'휴식',
'arg'=>null,
'brief'=>'휴식'
];
}
$db->insert('general_turn', $turnRows);
$rank_data = [];
foreach(array_keys(General::RANK_COLUMN) as $rankColumn){
$rank_data[] = [
'general_id'=>$generalID,
'nation_id'=>0,
'type'=>$rankColumn,
'value'=>0
];
}
$db->insert('rank_data', $rank_data);
$db->insert('betting', [
'general_id'=>$generalID,
]);
}
foreach($env as $key=>$value){
$gameStor->$key = $value;
}
$db->insert('ng_games', [
'server_id'=>$serverID,
'date'=>$turntime,
'winner_nation'=>null,
'map'=>$scenarioObj->getMapTheme(),
'season'=>$seasonIdx,
'scenario'=>$scenario,
'scenario_name'=>$scenarioObj->getTitle(),
'env'=>Json::encode($env)
]);
$scenarioObj->build($env);
$db->update('plock', [
'plock'=>0
], true);
LogHistory(1);
$prefix = DB::prefix();
AppConf::getList()[$prefix]->closeServer();
opcache_reset();
return [
'result'=>true
];
}
<?php
namespace sammo;
class ResetHelper{
private function __construct(){
}
static public function clearDB(){
$servRoot = realpath(__DIR__.'/../');
if(!file_exists($servRoot.'/logs') || !file_exists($servRoot.'/data')){
if(!is_writable($servRoot)){
return [
'result'=>false,
'reason'=>'logs, data 디렉토리를 생성할 권한이 없습니다.'
];
}
mkdir($servRoot.'/logs', 0755);
mkdir($servRoot.'/data', 0755);
}
if(!is_writable($servRoot.'/logs')){
return [
'result'=>false,
'reason'=>'logs 디렉토리의 쓰기 권한이 없습니다'
];
}
if(!is_writable($servRoot.'/data')){
return [
'result'=>false,
'reason'=>'data 디렉토리의 쓰기 권한이 없습니다'
];
}
if(!is_writable($servRoot.'/d_setting')){
return [
'result'=>false,
'reason'=>'d_setting 디렉토리의 쓰기 권한이 없습니다'
];
}
if(!file_exists($servRoot.'/logs/preserved')){
mkdir($servRoot.'/logs/preserved', 0755);
}
if(!file_exists($servRoot.'/logs/.htaccess')){
@file_put_contents($servRoot.'/logs/.htaccess', 'Deny from all');
}
if(!file_exists($servRoot.'/data/.htaccess')){
@file_put_contents($servRoot.'/data/.htaccess', 'Deny from all');
}
$dir = new \DirectoryIterator($servRoot.'/logs');
foreach ($dir as $fileinfo) {
/** @var \DirectoryIterator $fileinfo */
if (!$fileinfo->isDir() || $fileinfo->isDot()) {
continue;
}
$basename = $fileinfo->getFilename();
if($basename == 'preserved'){
continue;
}
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
exec("move ".escapeshellarg($servRoot.'/logs/'.$basename)." ".escapeshellarg($servRoot.'/logs/preserved/'.$basename));
} else {
exec("mv ".escapeshellarg($servRoot.'/logs/'.$basename)." ".escapeshellarg($servRoot.'/logs/preserved/'.$basename));
}
}
$prefix = DB::prefix();
ServConfig::getServerList()[$prefix]->closeServer();
$db = DB::db();
$mysqli_obj = $db->get();
$serverID = DB::prefix().'_'.date("ymd").'_'.Util::randomStr(4);
mkdir($servRoot.'/logs/'.$serverID, 0755);
mkdir($servRoot.'/data/'.$serverID, 0755);
$seasonIdx = 1;
if($db->queryFirstField('SHOW TABLES LIKE %s', 'storage') !== null){
$gameStor = KVStorage::getStorage($db, 'game_env');
$nextSeasonIdx = $gameStor->next_season_idx;
if($nextSeasonIdx !== null){
$seasonIdx = $nextSeasonIdx;
}
$gameStor->resetCache();
}
$result = Util::generateFileUsingSimpleTemplate(
$servRoot.'/d_setting/UniqueConst.orig.php',
$servRoot.'/d_setting/UniqueConst.php',[
'serverID'=>$serverID,
'serverName'=>ServConfig::getServerList()[$prefix]->getKorName(),
'seasonIdx'=>$seasonIdx,
], true
);
copy(
$servRoot.'/d_setting/GameConst.orig.php',
$servRoot.'/d_setting/GameConst.php',
);
if($mysqli_obj->multi_query(file_get_contents($servRoot.'/sql/reset.sql'))){
while(true){
if (!$mysqli_obj->more_results()) {
break;
}
if(!$mysqli_obj->next_result()){
break;
}
}
}
if($mysqli_obj->multi_query(file_get_contents($servRoot.'/sql/schema.sql'))){
while(true){
if (!$mysqli_obj->more_results()) {
break;
}
if(!$mysqli_obj->next_result()){
break;
}
}
}
$gameStor = KVStorage::getStorage($db, 'game_env');
$gameStor->resetValues();
$gameStor->next_season_idx = $seasonIdx;
return [
'result'=>true,
'serverID'=>$serverID,
'seasonIdx'=>$seasonIdx
];
}
static public function buildScenario(
int $turnterm,
int $sync,
int $scenario,
int $fiction,
int $extend,
int $npcmode,
int $show_img_level,
int $tournament_trig,
string $join_mode,
string $turntime,
?array $autorun_user
):array{
//FIXME: 분리할 것
if(120 % $turnterm != 0){
return [
'result'=>false,
'reason'=>'turnterm은 120의 약수여야 합니다.'
];
}
if($tournament_trig < 0 || $tournament_trig > 7){
return [
'result'=>false,
'reason'=>'올바르지 않은 토너먼트 주기입니다.'
];
}
$clearResult = self::clearDB();
if(!$clearResult['result']){
return $clearResult;
}
$serverID = $clearResult['serverID'];
$seasonIdx = $clearResult['seasonIdx'];
$scenarioObj = new Scenario($scenario, false);
$scenarioObj->buildConf();
$startyear = $scenarioObj->getYear()??GameConst::$defaultStartYear;
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$db->insert('plock', [
'plock'=>1,
'locktime'=>TimeUtil::now(true)
]);
$prevWinner = $db->queryFirstField('SELECT l12name FROM emperior ORDER BY `no` DESC LIMIT 1');
$serverCnt = $db->queryFirstField('SELECT count(*) FROM ng_games') + 1;
CityConst::build();
$cityPositions = [];
foreach(CityConst::all() as $city){
$cityPositions[$city->id] = [
$city->name,
$city->posX,
$city->posY
];
}
Util::generateFileUsingSimpleTemplate(
__DIR__.'/../templates/base_map.orig.js',
__DIR__.'/../d_shared/base_map.js',
[
'cityPosition'=>Json::encode($cityPositions),
'regionMap'=>Json::encode(CityConst::$regionMap),
'levelMap'=>Json::encode(CityConst::$levelMap),
],
true
);
if($sync == 0) {
// 현재 시간을 1월로 맞춤
$starttime = cutTurn($turntime, $turnterm);
$month = 1;
$year = $startyear;
} else {
// 현재 시간과 동기화
[$starttime, $yearPulled, $month] = cutDay($turntime, $turnterm);
if($yearPulled){
$year = $startyear-1;
}
else{
$year = $startyear;
}
}
$killturn = 4800 / $turnterm;
if($npcmode == 1) { $killturn = intdiv($killturn, 3); }
$develcost = ($year - $startyear + 10) * 2;
$env = [
'scenario'=>$scenario,
'scenario_text'=>$scenarioObj->getTitle(),
'icon_path'=>$scenarioObj->getIconPath(),
'startyear'=>$startyear,
'year'=> $year,
'month'=> $month,
'init_year'=> $year,
'init_month'=>$month,
'map_theme' => $scenarioObj->getMapTheme(),
'season'=>$seasonIdx,
'msg'=>'공지사항',//TODO:공지사항
'maxgeneral'=>GameConst::$defaultMaxGeneral,
'maxnation'=>GameConst::$defaultMaxNation,
'conlimit'=>30000,
'develcost'=>$develcost,
'turntime'=>$turntime,
'starttime'=>$starttime,
'opentime'=>$turntime,
'turnterm'=>$turnterm,
'killturn'=>$killturn,
'genius'=>GameConst::$defaultMaxGenius,
'show_img_level'=>$show_img_level,
'join_mode'=>$join_mode,
'npcmode'=>$npcmode,
'extended_general'=>$extend,
'fiction'=>$fiction,
'tnmt_trig'=>$tournament_trig,
'prev_winner'=>$prevWinner,
'autorun_user'=>$autorun_user,
'tournament'=>0,
'server_cnt'=>$serverCnt,
];
$db->insert('betting', [
'general_id'=>0,
'bet0'=>0,
'bet1'=>0,
'bet2'=>0,
'bet3'=>0,
'bet4'=>0,
'bet5'=>0,
'bet6'=>0,
'bet7'=>0,
'bet8'=>0,
'bet9'=>0,
'bet10'=>0,
'bet11'=>0,
'bet12'=>0,
'bet13'=>0,
'bet14'=>0,
'bet15'=>0,
]);
foreach(RootDB::db()->query('SELECT `no`, `name`, `picture`, `imgsvr` FROM member WHERE grade >= 5') as $admin){
$db->insert('general', [
'owner'=>$admin['no'],
'name'=>$admin['name'],
'picture'=>$admin['picture'],
'imgsvr'=>$admin['imgsvr'],
'turntime'=>$turntime,
'killturn'=>9999,
'crewtype'=>GameUnitConst::DEFAULT_CREWTYPE
]);
$generalID = $db->insertId();
$turnRows = [];
foreach(Util::range(GameConst::$maxTurn) as $turnIdx){
$turnRows[] = [
'general_id'=>$generalID,
'turn_idx'=>$turnIdx,
'action'=>'휴식',
'arg'=>null,
'brief'=>'휴식'
];
}
$db->insert('general_turn', $turnRows);
$rank_data = [];
foreach(array_keys(General::RANK_COLUMN) as $rankColumn){
$rank_data[] = [
'general_id'=>$generalID,
'nation_id'=>0,
'type'=>$rankColumn,
'value'=>0
];
}
$db->insert('rank_data', $rank_data);
$db->insert('betting', [
'general_id'=>$generalID,
]);
}
foreach($env as $key=>$value){
$gameStor->$key = $value;
}
$db->insert('ng_games', [
'server_id'=>$serverID,
'date'=>$turntime,
'winner_nation'=>null,
'map'=>$scenarioObj->getMapTheme(),
'season'=>$seasonIdx,
'scenario'=>$scenario,
'scenario_name'=>$scenarioObj->getTitle(),
'env'=>Json::encode($env)
]);
$scenarioObj->build($env);
$db->update('plock', [
'plock'=>0
], true);
LogHistory(1);
$prefix = DB::prefix();
ServConfig::getServerList()[$prefix]->closeServer();
opcache_reset();
return [
'result'=>true
];
}
}
+90 -90
View File
@@ -1,90 +1,90 @@
<?php
namespace sammo;
require(__DIR__.'/../vendor/autoload.php');
$session = Session::requireLogin([])->setReadOnly();
$userID = Session::getUserID();
// 외부 파라미터
$db = RootDB::db();
$member = $db->queryFirstRow('SELECT `id`, `name`, `grade`, `picture`, reg_date, third_use, acl, oauth_type, token_valid_until FROM `member` WHERE `NO` = %i', $userID);
if(!$member['picture']){
$picture = ServConfig::getSharedIconPath().'/default.jpg';
}
else{
$picture = $member['picture'];
if(strlen($picture) > 11){
$picture = substr($picture, 0, -10);
}
$pictureFSPath = AppConf::getUserIconPathFS().'/'.$picture;
if(file_exists($pictureFSPath)){
$picture = AppConf::getUserIconPathWeb().'/'.$picture;
}
else{
$picture = ServConfig::getSharedIconPath().'/'.$picture;
}
}
$tokenValidUntil = $member['token_valid_until'];
if($member['grade'] == 6) {
$grade = '운영자';
} elseif($member['grade'] == 5) {
$grade = '부운영자';
} elseif($member['grade'] > 1) {
$grade = '특별회원';
} elseif($member['grade'] == 1) {
$grade = '일반회원';
} elseif($member['grade'] == 0) {
$grade = '블럭회원';
}
$acl = [];
//TODO: Acl을 관리하기 위한 별도의 객체 필요
foreach(Json::decode($member['acl']??'{}') as $serverName=>$aclList){
$serverKorName = AppConf::getList()[$serverName]->getKorName();
$aclTextList = array_map(function($aclName){
$aclText = "알수없음[{$aclName}]";
switch($aclName){
case 'openClose':$aclText='서버여닫기';break;
case 'reset':$aclText='서버리셋';break;
case 'update':$aclText='업데이트';break;
case 'fullUpdate':$aclText='임의업데이트';break;
case 'vote':$aclText='설문조사';break;
case 'globalNotice':$aclText='전역공지';break;
case 'notice':$aclText='공지';break;
case 'blockGeneral':$aclText='장수징계';break;
}
return $aclText;
}, $aclList);
$acl[] = sprintf("%s(%s)", $serverKorName, join(",", $aclTextList));
}
if($acl){
$acl = join(",<br>", $acl);
}
else{
$acl = "-";
}
Json::die([
'result'=>true,
'reason'=>'success',
'id'=>$member['id'],
'name'=>$member['name'],
'grade'=>$grade,
'picture'=>$picture,
'global_salt'=>RootDB::getGlobalSalt(),
'join_date'=>$member['reg_date'],
'third_use'=>($member['third_use']!=0),
'acl'=>$acl,
'oauth_type'=>$member['oauth_type'],
'token_valid_until'=>$tokenValidUntil
]);
<?php
namespace sammo;
require(__DIR__.'/../vendor/autoload.php');
$session = Session::requireLogin([])->setReadOnly();
$userID = Session::getUserID();
// 외부 파라미터
$db = RootDB::db();
$member = $db->queryFirstRow('SELECT `id`, `name`, `grade`, `picture`, reg_date, third_use, acl, oauth_type, token_valid_until FROM `member` WHERE `NO` = %i', $userID);
if(!$member['picture']){
$picture = ServConfig::getSharedIconPath().'/default.jpg';
}
else{
$picture = $member['picture'];
if(strlen($picture) > 11){
$picture = substr($picture, 0, -10);
}
$pictureFSPath = AppConf::getUserIconPathFS().'/'.$picture;
if(file_exists($pictureFSPath)){
$picture = AppConf::getUserIconPathWeb().'/'.$picture;
}
else{
$picture = ServConfig::getSharedIconPath().'/'.$picture;
}
}
$tokenValidUntil = $member['token_valid_until'];
if($member['grade'] == 6) {
$grade = '운영자';
} elseif($member['grade'] == 5) {
$grade = '부운영자';
} elseif($member['grade'] > 1) {
$grade = '특별회원';
} elseif($member['grade'] == 1) {
$grade = '일반회원';
} elseif($member['grade'] == 0) {
$grade = '블럭회원';
}
$acl = [];
//TODO: Acl을 관리하기 위한 별도의 객체 필요
foreach(Json::decode($member['acl']??'{}') as $serverName=>$aclList){
$serverKorName = ServConfig::getServerList()[$serverName]->getKorName();
$aclTextList = array_map(function($aclName){
$aclText = "알수없음[{$aclName}]";
switch($aclName){
case 'openClose':$aclText='서버여닫기';break;
case 'reset':$aclText='서버리셋';break;
case 'update':$aclText='업데이트';break;
case 'fullUpdate':$aclText='임의업데이트';break;
case 'vote':$aclText='설문조사';break;
case 'globalNotice':$aclText='전역공지';break;
case 'notice':$aclText='공지';break;
case 'blockGeneral':$aclText='장수징계';break;
}
return $aclText;
}, $aclList);
$acl[] = sprintf("%s(%s)", $serverKorName, join(",", $aclTextList));
}
if($acl){
$acl = join(",<br>", $acl);
}
else{
$acl = "-";
}
Json::die([
'result'=>true,
'reason'=>'success',
'id'=>$member['id'],
'name'=>$member['name'],
'grade'=>$grade,
'picture'=>$picture,
'global_salt'=>RootDB::getGlobalSalt(),
'join_date'=>$member['reg_date'],
'third_use'=>($member['third_use']!=0),
'acl'=>$acl,
'oauth_type'=>$member['oauth_type'],
'token_valid_until'=>$tokenValidUntil
]);
+57 -57
View File
@@ -1,58 +1,58 @@
<?php
namespace sammo;
require(__DIR__.'/../vendor/autoload.php');
$session = Session::requireLogin([])->setReadOnly();
$db = RootDB::db();
if($session->userGrade < 5){
Json::die([
'result'=>false,
'reason'=>'권한이 부족합니다'
]);
}
$userList = [];
foreach($db->query('SELECT member.*, max(member_log.date) as loginDate from member
left join member_log on member.`NO` = member_log.member_no and member_log.action_type="login"
group by member.no order by member.no asc') as $member){
if($member['IMGSVR']){
$icon = AppConf::getUserIconPathWeb().'/'.$member['PICTURE'];
}
else{
$icon = ServConfig::getSharedIconPath().'/'.$member['PICTURE'];
}
$userList[] = [
'userID'=>$member['NO'],
'userName'=>$member['ID'],
'email'=>$member['EMAIL'],
'authType'=>$member['oauth_type'],
'userGrade'=>$member['GRADE'],
'blockUntil'=>$member['BLOCK_DATE'],
'nickname'=>$member['NAME'],
'icon'=>$icon,
'joinDate'=>$member['REG_DATE'],
'loginDate'=>$member['loginDate'],
'deleteAfter'=>$member['delete_after']
];
}
$serverList = [];
foreach(AppConf::getList() as $serverName => $setting){
if($setting->isRunning()){
$serverList[] = $serverName;
}
}
$system = $db->queryFirstRow('SELECT `REG`, `LOGIN` FROM `system` limit 1');
Json::die([
'result'=>true,
'users'=>$userList,
'servers'=>$serverList,
'allowJoin'=>$system['REG']=='Y',
'allowLogin'=>$system['LOGIN']=='Y'
<?php
namespace sammo;
require(__DIR__.'/../vendor/autoload.php');
$session = Session::requireLogin([])->setReadOnly();
$db = RootDB::db();
if($session->userGrade < 5){
Json::die([
'result'=>false,
'reason'=>'권한이 부족합니다'
]);
}
$userList = [];
foreach($db->query('SELECT member.*, max(member_log.date) as loginDate from member
left join member_log on member.`NO` = member_log.member_no and member_log.action_type="login"
group by member.no order by member.no asc') as $member){
if($member['IMGSVR']){
$icon = AppConf::getUserIconPathWeb().'/'.$member['PICTURE'];
}
else{
$icon = ServConfig::getSharedIconPath().'/'.$member['PICTURE'];
}
$userList[] = [
'userID'=>$member['NO'],
'userName'=>$member['ID'],
'email'=>$member['EMAIL'],
'authType'=>$member['oauth_type'],
'userGrade'=>$member['GRADE'],
'blockUntil'=>$member['BLOCK_DATE'],
'nickname'=>$member['NAME'],
'icon'=>$icon,
'joinDate'=>$member['REG_DATE'],
'loginDate'=>$member['loginDate'],
'deleteAfter'=>$member['delete_after']
];
}
$serverList = [];
foreach(ServConfig::getServerList() as $serverName => $setting){
if($setting->isRunning()){
$serverList[] = $serverName;
}
}
$system = $db->queryFirstRow('SELECT `REG`, `LOGIN` FROM `system` limit 1');
Json::die([
'result'=>true,
'users'=>$userList,
'servers'=>$serverList,
'allowJoin'=>$system['REG']=='Y',
'allowLogin'=>$system['LOGIN']=='Y'
]);
+106 -106
View File
@@ -1,107 +1,107 @@
<?php
namespace sammo;
require(__DIR__.'/../vendor/autoload.php');
WebUtil::requireAJAX();
$session = Session::requireLogin([])->setReadOnly();
$userID = Session::getUserID();
// 외부 파라미터
// $_FILES['image_upload'] : 사진파일
$defaultImg = 'default.jpg';
$image = $_FILES['image_upload'];
$ext = strrchr($image['name'], ".");
$size = getImageSize($image['tmp_name']);
$imageType = $size[2];
$availableImageType = array('.jpg'=>IMAGETYPE_JPEG, '.png'=>IMAGETYPE_PNG, '.gif'=>IMAGETYPE_GIF);
$db = RootDB::db();
$member = $db->queryFirstRow('SELECT `ID`, `PICTURE` FROM `member` WHERE `NO` = %i', $userID);
$picName = $member['PICTURE'];
$newExt = array_search($imageType, $availableImageType, true);
if($picName && strlen($picName) > 11){
$dt = substr($picName, -8);
$picName = substr($picName, 0, -10);
}
else{
$dt = '00000000';
}
$rf = date('Ymd');
$response['result'] = false;
$response['reason'] = '요청이 올바르지 않습니다!';
if(!is_uploaded_file($image['tmp_name'])) {
//진짜 전송된 파일인지 검증
$response['reason'] = '업로드가 되지 않았습니다!';
$response['result'] = false;
} elseif(!$newExt) {
//확장자 검사
$response['reason'] = 'jpg, gif, png 파일이 아닙니다!';
$response['result'] = false;
} elseif($image['size'] > 30720) {
//파일크기 검사
$response['reason'] = '30kb 이하로 올려주세요!';
$response['result'] = false;
} elseif($size[0] < 64 || 128 < $size[0]) {
//이미지크기 검사
$response['reason'] = '64x64 ~ 128x128 사이여야 합니다.';
$response['result'] = false;
} elseif($size[0] != $size[1]) {
//이미지크기 검사
$response['reason'] = '1:1 비율 이미지여야 합니다.';
$response['result'] = false;
}elseif($dt == $rf) {
//갱신날짜 검사
$response['reason'] = '1일 1회 변경 가능합니다!';
$response['result'] = false;
} else {
//이미지 저장
while(true){
$newPicName = dechex(rand(0x000000f,0xfffffff)).$newExt;
$dest = AppConf::getUserIconPathFS().'/'.$newPicName;
if(file_exists($dest)){
continue;
}
break;
}
if(!move_uploaded_file($image['tmp_name'], $dest)) {
$response['reason'] = '업로드에 실패했습니다!';
$response['result'] = false;
} else {
$pic = "{$newPicName}?={$rf}";
RootDB::db()->update('member',[
'PICTURE' => $pic,
'IMGSVR' => 1
], 'NO=%i', $userID);
$servers = [];
foreach(AppConf::getList() as $key=>$setting){
if($setting->isRunning()){
$servers[] = [$key, $setting->getKorName()];
}
}
$response['servers'] = $servers;
$response['reason'] = '업로드에 성공했습니다!';
$response['result'] = true;
}
}
<?php
namespace sammo;
require(__DIR__.'/../vendor/autoload.php');
WebUtil::requireAJAX();
$session = Session::requireLogin([])->setReadOnly();
$userID = Session::getUserID();
// 외부 파라미터
// $_FILES['image_upload'] : 사진파일
$defaultImg = 'default.jpg';
$image = $_FILES['image_upload'];
$ext = strrchr($image['name'], ".");
$size = getImageSize($image['tmp_name']);
$imageType = $size[2];
$availableImageType = array('.jpg'=>IMAGETYPE_JPEG, '.png'=>IMAGETYPE_PNG, '.gif'=>IMAGETYPE_GIF);
$db = RootDB::db();
$member = $db->queryFirstRow('SELECT `ID`, `PICTURE` FROM `member` WHERE `NO` = %i', $userID);
$picName = $member['PICTURE'];
$newExt = array_search($imageType, $availableImageType, true);
if($picName && strlen($picName) > 11){
$dt = substr($picName, -8);
$picName = substr($picName, 0, -10);
}
else{
$dt = '00000000';
}
$rf = date('Ymd');
$response['result'] = false;
$response['reason'] = '요청이 올바르지 않습니다!';
if(!is_uploaded_file($image['tmp_name'])) {
//진짜 전송된 파일인지 검증
$response['reason'] = '업로드가 되지 않았습니다!';
$response['result'] = false;
} elseif(!$newExt) {
//확장자 검사
$response['reason'] = 'jpg, gif, png 파일이 아닙니다!';
$response['result'] = false;
} elseif($image['size'] > 30720) {
//파일크기 검사
$response['reason'] = '30kb 이하로 올려주세요!';
$response['result'] = false;
} elseif($size[0] < 64 || 128 < $size[0]) {
//이미지크기 검사
$response['reason'] = '64x64 ~ 128x128 사이여야 합니다.';
$response['result'] = false;
} elseif($size[0] != $size[1]) {
//이미지크기 검사
$response['reason'] = '1:1 비율 이미지여야 합니다.';
$response['result'] = false;
}elseif($dt == $rf) {
//갱신날짜 검사
$response['reason'] = '1일 1회 변경 가능합니다!';
$response['result'] = false;
} else {
//이미지 저장
while(true){
$newPicName = dechex(rand(0x000000f,0xfffffff)).$newExt;
$dest = AppConf::getUserIconPathFS().'/'.$newPicName;
if(file_exists($dest)){
continue;
}
break;
}
if(!move_uploaded_file($image['tmp_name'], $dest)) {
$response['reason'] = '업로드에 실패했습니다!';
$response['result'] = false;
} else {
$pic = "{$newPicName}?={$rf}";
RootDB::db()->update('member',[
'PICTURE' => $pic,
'IMGSVR' => 1
], 'NO=%i', $userID);
$servers = [];
foreach(ServConfig::getServerList() as $key=>$setting){
if($setting->isRunning()){
$servers[] = [$key, $setting->getKorName()];
}
}
$response['servers'] = $servers;
$response['reason'] = '업로드에 성공했습니다!';
$response['result'] = true;
}
}
Json::die($response);
+55 -55
View File
@@ -1,56 +1,56 @@
<?php
namespace sammo;
require(__DIR__.'/../vendor/autoload.php');
WebUtil::requireAJAX();
$session = Session::requireLogin([])->setReadOnly();
$userID = Session::getUserID();
// 외부 파라미터
$respone = [];
$db = RootDB::db();
$picName = $db->queryFirstField('SELECT picture FROM `member` WHERE `NO` = %i', $userID);
if($picName && strlen($picName) > 11){
$dt = substr($picName, -8);
$picName = substr($picName, 0, -10);
}
else{
$dt = '00000000';
}
$dest = AppConf::getUserIconPathFS().'/'.$picName;
$rf = date('Ymd');
$response['result'] = false;
$response['reason'] = '요청이 올바르지 않습니다!';
if($dt == $rf) {
//갱신날짜 검사
$response['reason'] = '1일 1회 변경 가능합니다!';
$response['result'] = false;
} else {
$db->update('member', [
'PICTURE'=>'default.jpg',
'IMGSVR'=>0,
], 'NO=%i', $userID);
$servers = [];
foreach(AppConf::getList() as $key=>$setting){
if($setting->isRunning()){
$servers[] = [$key, $setting->getKorName()];
}
}
$response['servers'] = $servers;
$response['reason'] = '제거에 성공했습니다!';
$response['result'] = true;
}
<?php
namespace sammo;
require(__DIR__.'/../vendor/autoload.php');
WebUtil::requireAJAX();
$session = Session::requireLogin([])->setReadOnly();
$userID = Session::getUserID();
// 외부 파라미터
$respone = [];
$db = RootDB::db();
$picName = $db->queryFirstField('SELECT picture FROM `member` WHERE `NO` = %i', $userID);
if($picName && strlen($picName) > 11){
$dt = substr($picName, -8);
$picName = substr($picName, 0, -10);
}
else{
$dt = '00000000';
}
$dest = AppConf::getUserIconPathFS().'/'.$picName;
$rf = date('Ymd');
$response['result'] = false;
$response['reason'] = '요청이 올바르지 않습니다!';
if($dt == $rf) {
//갱신날짜 검사
$response['reason'] = '1일 1회 변경 가능합니다!';
$response['result'] = false;
} else {
$db->update('member', [
'PICTURE'=>'default.jpg',
'IMGSVR'=>0,
], 'NO=%i', $userID);
$servers = [];
foreach(ServConfig::getServerList() as $key=>$setting){
if($setting->isRunning()){
$servers[] = [$key, $setting->getKorName()];
}
}
$response['servers'] = $servers;
$response['reason'] = '제거에 성공했습니다!';
$response['result'] = true;
}
Json::die($response);
+117 -117
View File
@@ -1,117 +1,117 @@
<?php
namespace sammo;
require(__DIR__.'/../vendor/autoload.php');
WebUtil::requireAJAX();
$session = Session::requireLogin(null);
// 외부 파라미터
// $_POST['action'] : 'notice', 'open', 'close', 'reset', 'reset_full'
// $_POST['notice'] : 공지
// $_POST['server'] : 서버 인덱스
$action = Util::getPost('action', 'string', '');
$notice = Util::getPost('notice', 'string', '');
$server = Util::getPost('server', 'string', '');
$db = RootDB::db();
$userGrade = $session->userGrade;
$acl = $session->acl;
$session->setReadOnly();
if($userGrade < 5 && !$acl) {
Json::die([
'result'=>'FAIL',
'msg'=>'운영자 권한이 없습니다.'
]);
}
function doServerModeSet($server, $action, &$response, $session){
$serverList = AppConf::getList();
$settingObj = $serverList[$server];
$serverAcl = $session->acl[$server]??[];
$userGrade = $session->userGrade;
$serverDir = $settingObj->getShortName();
$serverPath = $settingObj->getBasePath();
$realServerPath = realpath(dirname(__FILE__)).'/'.$serverPath;
if($action == 'close') { //폐쇄
$doClose = false;
if($userGrade >= 5){
$doClose = true;
}
else if(in_array('openClose', $serverAcl)){
$doClose = true;
}
if(!$doClose && in_array('reset', $serverAcl) && file_exists($serverPath.'/d_setting/DB.php')){
require($serverPath.'/lib.php');
$localGameStorage = KVStorage::getStorage(DB::db(), 'game_env');
//천통 이후, 오픈 직후는 닫을 수 있음
$localGameStorage->cacheValues(['isunited', 'startyear', 'year']);
if($localGameStorage->isunited){
$doClose = true;
}
else if($localGameStorage->year < $localGameStorage->startyear + 2){
$doClose = true;
}
}
if(!$doClose){
if(in_array('reset', $serverAcl)){
$response['msg'] = '서버 시작 직후, 또는 천하통일 이후에만 닫을 수 있습니다.';
}
else{
$response['msg'] = '서버 닫기 권한이 부족합니다.';
}
return false;
}
return $settingObj->closeServer();
} elseif($action == 'reset' && $userGrade >= 6) {//리셋
//FIXME: reset, reset_full 구현
if(file_exists($serverPath.'/d_setting/DB.php')){
@unlink($serverPath.'/d_setting/DB.php');
}
$response['installURL'] = $serverDir."/install.php";
} elseif($action == 'open' && ($userGrade >= 5 || in_array('openClose', $serverAcl))) {//오픈
return $settingObj->openServer();
} else{
$response['msg'] = '올바르지 않은 요청입니다';
return false;
}
return true;
}
function doAdminPost($action, $notice, $server, $session){
$response = ['result' => 'FAIL'];
$globalAcl = $session->acl['global']??[];
$userGrade = $session->userGrade;
if($action == 'notice' && ($userGrade >= 5 || in_array('notice', $globalAcl))) {
RootDB::db()->update('system', ['NOTICE'=>$notice], true);
$response['result'] = 'SUCCESS';
return $response;
}
if(doServerModeSet($server, $action, $response, $session)){
$response['result'] = 'SUCCESS';
return $response;
}
return $response;
}
$response = doAdminPost($action, $notice, $server, $session);
Json::die($response);
<?php
namespace sammo;
require(__DIR__.'/../vendor/autoload.php');
WebUtil::requireAJAX();
$session = Session::requireLogin(null);
// 외부 파라미터
// $_POST['action'] : 'notice', 'open', 'close', 'reset', 'reset_full'
// $_POST['notice'] : 공지
// $_POST['server'] : 서버 인덱스
$action = Util::getPost('action', 'string', '');
$notice = Util::getPost('notice', 'string', '');
$server = Util::getPost('server', 'string', '');
$db = RootDB::db();
$userGrade = $session->userGrade;
$acl = $session->acl;
$session->setReadOnly();
if($userGrade < 5 && !$acl) {
Json::die([
'result'=>'FAIL',
'msg'=>'운영자 권한이 없습니다.'
]);
}
function doServerModeSet($server, $action, &$response, $session){
$serverList = ServConfig::getServerList();
$settingObj = $serverList[$server];
$serverAcl = $session->acl[$server]??[];
$userGrade = $session->userGrade;
$serverDir = $settingObj->getShortName();
$serverPath = $settingObj->getBasePath();
$realServerPath = realpath(dirname(__FILE__)).'/'.$serverPath;
if($action == 'close') { //폐쇄
$doClose = false;
if($userGrade >= 5){
$doClose = true;
}
else if(in_array('openClose', $serverAcl)){
$doClose = true;
}
if(!$doClose && in_array('reset', $serverAcl) && file_exists($serverPath.'/d_setting/DB.php')){
require($serverPath.'/lib.php');
$localGameStorage = KVStorage::getStorage(DB::db(), 'game_env');
//천통 이후, 오픈 직후는 닫을 수 있음
$localGameStorage->cacheValues(['isunited', 'startyear', 'year']);
if($localGameStorage->isunited){
$doClose = true;
}
else if($localGameStorage->year < $localGameStorage->startyear + 2){
$doClose = true;
}
}
if(!$doClose){
if(in_array('reset', $serverAcl)){
$response['msg'] = '서버 시작 직후, 또는 천하통일 이후에만 닫을 수 있습니다.';
}
else{
$response['msg'] = '서버 닫기 권한이 부족합니다.';
}
return false;
}
return $settingObj->closeServer();
} elseif($action == 'reset' && $userGrade >= 6) {//리셋
//FIXME: reset, reset_full 구현
if(file_exists($serverPath.'/d_setting/DB.php')){
@unlink($serverPath.'/d_setting/DB.php');
}
$response['installURL'] = $serverDir."/install.php";
} elseif($action == 'open' && ($userGrade >= 5 || in_array('openClose', $serverAcl))) {//오픈
return $settingObj->openServer();
} else{
$response['msg'] = '올바르지 않은 요청입니다';
return false;
}
return true;
}
function doAdminPost($action, $notice, $server, $session){
$response = ['result' => 'FAIL'];
$globalAcl = $session->acl['global']??[];
$userGrade = $session->userGrade;
if($action == 'notice' && ($userGrade >= 5 || in_array('notice', $globalAcl))) {
RootDB::db()->update('system', ['NOTICE'=>$notice], true);
$response['result'] = 'SUCCESS';
return $response;
}
if(doServerModeSet($server, $action, $response, $session)){
$response['result'] = 'SUCCESS';
return $response;
}
return $response;
}
$response = doAdminPost($action, $notice, $server, $session);
Json::die($response);
+89 -89
View File
@@ -1,90 +1,90 @@
<?php
namespace sammo;
require(__DIR__.'/../vendor/autoload.php');
WebUtil::requireAJAX();
$session = Session::requireLogin([])->setReadOnly();
if($session->userGrade < 5 && !$session->acl){
Json::die([
'result'=>false,
'reason'=>'권한이 부족합니다.'
]);
}
$result = [];
$server = [];
$storage = new \sammo\KVStorage(RootDB::db(), 'git_path');
$serverGitPath = $storage->getAll();
$rootServer = Util::array_last(AppConf::getList())->getShortName();
foreach (AppConf::getList() as $setting) {
$serverColor = $setting->getColor();
$serverKorName = $setting->getKorName();
$serverPath = $setting->getBasePath();
$serverDir = $setting->getShortName();
//TODO: .htaccess가 서버 오픈에도 사용될 수 있으니 별도의 방법이 필요함
if (!is_dir($serverPath)) {
$state = [
'valid'=>false,
'run'=>false,
'installed'=>false,
'version'=>$setting->getVersion(),
'reason'=>'디렉토리 없음'
];
} elseif (!file_exists($serverPath.'/index.php')) {
$state = [
'valid'=>false,
'run'=>false,
'installed'=>false,
'version'=>$setting->getVersion(),
'reason'=>'index.php 없음'
];
} elseif (!$setting->isExists()) {
$state = [
'valid'=>false,
'run'=>false,
'installed'=>true,
'version'=>$setting->getVersion(),
'reason'=>'설정 파일 없음'
];
} elseif (!$setting->isRunning()) {
// 폐쇄중
$state = [
'valid'=>true,
'run'=>false,
'installed'=>true,
'version'=>$setting->getVersion(),
'reason'=>'폐쇄됨'
];
} else {
// 오픈중
$state = [
'valid'=>true,
'run'=>true,
'installed'=>true,
'version'=>$setting->getVersion(),
'reason'=>'운영중'
];
}
$state += [
'name' => $serverDir,
'korName' => $serverKorName,
'color' => $serverColor,
'isRoot' => $serverDir == $rootServer,
'lastGitPath' => ($serverGitPath[$serverDir][0])??($serverDir == $rootServer?'devel':'origin/devel')
];
$server[] = $state;
}
Json::die([
'acl' => $session->acl,
'server' => $server,
'grade' => $session->userGrade
<?php
namespace sammo;
require(__DIR__.'/../vendor/autoload.php');
WebUtil::requireAJAX();
$session = Session::requireLogin([])->setReadOnly();
if($session->userGrade < 5 && !$session->acl){
Json::die([
'result'=>false,
'reason'=>'권한이 부족합니다.'
]);
}
$result = [];
$server = [];
$storage = new \sammo\KVStorage(RootDB::db(), 'git_path');
$serverGitPath = $storage->getAll();
$rootServer = Util::array_last(ServConfig::getServerList())->getShortName();
foreach (ServConfig::getServerList() as $setting) {
$serverColor = $setting->getColor();
$serverKorName = $setting->getKorName();
$serverPath = $setting->getBasePath();
$serverDir = $setting->getShortName();
//TODO: .htaccess가 서버 오픈에도 사용될 수 있으니 별도의 방법이 필요함
if (!is_dir($serverPath)) {
$state = [
'valid'=>false,
'run'=>false,
'installed'=>false,
'version'=>$setting->getVersion(),
'reason'=>'디렉토리 없음'
];
} elseif (!file_exists($serverPath.'/index.php')) {
$state = [
'valid'=>false,
'run'=>false,
'installed'=>false,
'version'=>$setting->getVersion(),
'reason'=>'index.php 없음'
];
} elseif (!$setting->isExists()) {
$state = [
'valid'=>false,
'run'=>false,
'installed'=>true,
'version'=>$setting->getVersion(),
'reason'=>'설정 파일 없음'
];
} elseif (!$setting->isRunning()) {
// 폐쇄중
$state = [
'valid'=>true,
'run'=>false,
'installed'=>true,
'version'=>$setting->getVersion(),
'reason'=>'폐쇄됨'
];
} else {
// 오픈중
$state = [
'valid'=>true,
'run'=>true,
'installed'=>true,
'version'=>$setting->getVersion(),
'reason'=>'운영중'
];
}
$state += [
'name' => $serverDir,
'korName' => $serverKorName,
'color' => $serverColor,
'isRoot' => $serverDir == $rootServer,
'lastGitPath' => ($serverGitPath[$serverDir][0])??($serverDir == $rootServer?'devel':'origin/devel')
];
$server[] = $state;
}
Json::die([
'acl' => $session->acl,
'server' => $server,
'grade' => $session->userGrade
]);
+25 -25
View File
@@ -1,25 +1,25 @@
<?php
namespace sammo;
require(__DIR__.'/../vendor/autoload.php');
// 외부 파라미터
$response['server'] = [];
foreach(AppConf::getList() as $setting){
$serverObj = [
'color'=>$setting->getColor(),
'korName'=>$setting->getKorName(),
'name'=>$setting->getShortName(),
'exists'=>$setting->isExists(),
'enable'=>$setting->isRunning()
];
$response['server'][] = $serverObj;
}
$response['result'] = 'SUCCESS';
Json::die($response);
<?php
namespace sammo;
require(__DIR__.'/../vendor/autoload.php');
// 외부 파라미터
$response['server'] = [];
foreach(ServConfig::getServerList() as $setting){
$serverObj = [
'color'=>$setting->getColor(),
'korName'=>$setting->getKorName(),
'name'=>$setting->getShortName(),
'exists'=>$setting->isExists(),
'enable'=>$setting->isRunning()
];
$response['server'][] = $serverObj;
}
$response['result'] = 'SUCCESS';
Json::die($response);
+240 -240
View File
@@ -1,240 +1,240 @@
<?php
namespace sammo;
require(__DIR__ . '/vendor/autoload.php');
WebUtil::requireAJAX();
function getVersion($target = null)
{
if ($target) {
$command = sprintf('git describe %s --long --tags', escapeshellarg($target));
} else {
$command = 'git describe --long --tags';
}
exec($command, $output);
if (is_array($output)) {
$output = join('', $output);
}
return trim($output);
}
$session = Session::requireLogin(null)->setReadOnly();
$request = $_POST + $_GET;
$rootDB = RootDB::db();
$storage = new \sammo\KVStorage($rootDB, 'git_path');
$tmpFile = 'd_log/arc.zip';
$v = new Validator($request);
$v->rule('required', [
'server'
])->rule('regex', 'target', '/^[0-9a-zA-Z^{}\\/\-_,.@]+$/');
if (!$v->validate()) {
Json::die([
'result' => false,
'reason' => $v->errorStr()
]);
}
$target = Util::getPost('target');
$server = basename($request['server']);
$allowFullUpdate = in_array('fullUpdate', $session->acl[$server] ?? []);
$allowFullUpdate |= $session->userGrade >= 6;
$allowUpdate = in_array('update', $session->acl[$server] ?? []);
$allowUpdate |= $session->userGrade >= 5;
$allowUpdate |= $allowFullUpdate;
if (!$allowUpdate) {
Json::die([
'result' => false,
'reason' => '권한이 충분하지 않습니다'
]);
}
$src_target = $storage->$server;
if ($src_target) {
$src_target = $src_target[0];
}
if (!$allowFullUpdate || !$target) {
$target = $src_target;
} else {
$target = $request['target'];
}
$baseServerName = Util::array_last_key(AppConf::getList());
if (!$target && $server != $baseServerName) {
Json::die([
'result' => false,
'reason' => 'git -ish target이 제대로 지정되지 않았습니다.'
]);
}
$targetDir = $target . ':' . $baseServerName;
if (!key_exists($server, AppConf::getList())) {
Json::die([
'result' => false,
'reason' => '불가능한 서버 이름입니다.'
]);
}
if (\file_exists($server) && !is_dir($server)) {
Json::die([
'result' => false,
'reason' => '같은 이름을 가진 파일이 있습니다.'
]);
}
if (file_exists($server) && !is_writable($server)) {
Json::die([
'result' => false,
'reason' => $server . ' 디렉토리 쓰기 권한이 없습니다.'
]);
}
if (!file_exists($server)) {
if (!is_writable('.')) {
Json::die([
'result' => false,
'reason' => $server . ' 디렉토리가 없지만 생성할 권한이 없습니다.'
]);
}
mkdir($server, 0755);
}
if ($server == $baseServerName) {
exec("git fetch -q 2>&1", $output);
if ($output) {
Json::die([
'result' => false,
'reason' => 'git pull 작업 : ' . join(', ', $output)
]);
}
if ($target != $src_target) {
$command = sprintf('git checkout %s -q 2>&1', $target);
exec($command, $output);
if ($output) {
Json::die([
'result' => false,
'reason' => join(', ', $output)
]);
}
}
exec("git pull -q 2>&1", $output);
if ($output && $output[0] != 'Already up-to-date.') {
Json::die([
'result' => false,
'reason' => 'git pull 작업 : ' . join(', ', $output)
]);
}
$version = getVersion();
$result = Util::generateFileUsingSimpleTemplate(
__DIR__ . '/' . $server . '/d_setting/VersionGit.orig.php',
__DIR__ . '/' . $server . '/d_setting/VersionGit.php',
[
'verionGit' => $version
],
true
);
if (ServConfig::$imageRequestKey) {
try {
$imagePullPath = ServConfig::getImagePullURI();
$pullResult = @file_get_contents($imagePullPath);
if ($pullResult === false) {
throw new \ErrorException('Invalid URI');
}
$pullResult = Json::decode($pullResult);
if ($pullResult['result']) {
$imgResult = true;
$imgDetail = $pullResult['version'];
} else {
$imgResult = false;
$imgDetail = $pullResult['reason'];
}
} catch (\Exception $e) {
$imgResult = false;
$imgDetail = $e->getMessage();
}
} else {
$imgResult = true;
$imgDetail = 'No key';
}
$storage->$server = [$target, $version];
opcache_reset();
Json::die([
'server' => $server,
'result' => true,
'version' => $version,
'imgResult' => $imgResult,
'imgDetail' => $imgDetail,
]);
}
$command = sprintf('git archive --format=zip -o %s %s', escapeshellarg($tmpFile), escapeshellarg($targetDir));
exec("$command 2>&1", $output);
if ($output) {
Json::die([
'result' => false,
'reason' => join(', ', $output)
]);
}
$zip = new \ZipArchive;
if ($zip->open($tmpFile) !== true) {
Json::die([
'result' => false,
'reason' => 'archive가 생성되지 않았습니다.'
]);
}
if (!$zip->extractTo($server)) {
Json::die([
'result' => false,
'reason' => '생성한 archive를 제대로 옮기지 못했습니다.'
]);
}
$zip->close();
@unlink($tmpFile);
$version = getVersion($target);
$result = Util::generateFileUsingSimpleTemplate(
__DIR__ . '/' . $server . '/d_setting/VersionGit.orig.php',
__DIR__ . '/' . $server . '/d_setting/VersionGit.php',
[
'verionGit' => $version
],
true
);
$storage->$server = [$target, $version];
//AppConf::getList()[$server]->closeServer();
opcache_reset();
Json::die([
'server' => $server,
'result' => true,
'version' => $version
]);
<?php
namespace sammo;
require(__DIR__ . '/vendor/autoload.php');
WebUtil::requireAJAX();
function getVersion($target = null)
{
if ($target) {
$command = sprintf('git describe %s --long --tags', escapeshellarg($target));
} else {
$command = 'git describe --long --tags';
}
exec($command, $output);
if (is_array($output)) {
$output = join('', $output);
}
return trim($output);
}
$session = Session::requireLogin(null)->setReadOnly();
$request = $_POST + $_GET;
$rootDB = RootDB::db();
$storage = new \sammo\KVStorage($rootDB, 'git_path');
$tmpFile = 'd_log/arc.zip';
$v = new Validator($request);
$v->rule('required', [
'server'
])->rule('regex', 'target', '/^[0-9a-zA-Z^{}\\/\-_,.@]+$/');
if (!$v->validate()) {
Json::die([
'result' => false,
'reason' => $v->errorStr()
]);
}
$target = Util::getPost('target');
$server = basename($request['server']);
$allowFullUpdate = in_array('fullUpdate', $session->acl[$server] ?? []);
$allowFullUpdate |= $session->userGrade >= 6;
$allowUpdate = in_array('update', $session->acl[$server] ?? []);
$allowUpdate |= $session->userGrade >= 5;
$allowUpdate |= $allowFullUpdate;
if (!$allowUpdate) {
Json::die([
'result' => false,
'reason' => '권한이 충분하지 않습니다'
]);
}
$src_target = $storage->$server;
if ($src_target) {
$src_target = $src_target[0];
}
if (!$allowFullUpdate || !$target) {
$target = $src_target;
} else {
$target = $request['target'];
}
$baseServerName = Util::array_last_key(ServConfig::getServerList());
if (!$target && $server != $baseServerName) {
Json::die([
'result' => false,
'reason' => 'git -ish target이 제대로 지정되지 않았습니다.'
]);
}
$targetDir = $target . ':' . $baseServerName;
if (!key_exists($server, ServConfig::getServerList())) {
Json::die([
'result' => false,
'reason' => '불가능한 서버 이름입니다.'
]);
}
if (\file_exists($server) && !is_dir($server)) {
Json::die([
'result' => false,
'reason' => '같은 이름을 가진 파일이 있습니다.'
]);
}
if (file_exists($server) && !is_writable($server)) {
Json::die([
'result' => false,
'reason' => $server . ' 디렉토리 쓰기 권한이 없습니다.'
]);
}
if (!file_exists($server)) {
if (!is_writable('.')) {
Json::die([
'result' => false,
'reason' => $server . ' 디렉토리가 없지만 생성할 권한이 없습니다.'
]);
}
mkdir($server, 0755);
}
if ($server == $baseServerName) {
exec("git fetch -q 2>&1", $output);
if ($output) {
Json::die([
'result' => false,
'reason' => 'git pull 작업 : ' . join(', ', $output)
]);
}
if ($target != $src_target) {
$command = sprintf('git checkout %s -q 2>&1', $target);
exec($command, $output);
if ($output) {
Json::die([
'result' => false,
'reason' => join(', ', $output)
]);
}
}
exec("git pull -q 2>&1", $output);
if ($output && $output[0] != 'Already up-to-date.') {
Json::die([
'result' => false,
'reason' => 'git pull 작업 : ' . join(', ', $output)
]);
}
$version = getVersion();
$result = Util::generateFileUsingSimpleTemplate(
__DIR__ . '/' . $server . '/d_setting/VersionGit.orig.php',
__DIR__ . '/' . $server . '/d_setting/VersionGit.php',
[
'verionGit' => $version
],
true
);
if (ServConfig::$imageRequestKey) {
try {
$imagePullPath = ServConfig::getImagePullURI();
$pullResult = @file_get_contents($imagePullPath);
if ($pullResult === false) {
throw new \ErrorException('Invalid URI');
}
$pullResult = Json::decode($pullResult);
if ($pullResult['result']) {
$imgResult = true;
$imgDetail = $pullResult['version'];
} else {
$imgResult = false;
$imgDetail = $pullResult['reason'];
}
} catch (\Exception $e) {
$imgResult = false;
$imgDetail = $e->getMessage();
}
} else {
$imgResult = true;
$imgDetail = 'No key';
}
$storage->$server = [$target, $version];
opcache_reset();
Json::die([
'server' => $server,
'result' => true,
'version' => $version,
'imgResult' => $imgResult,
'imgDetail' => $imgDetail,
]);
}
$command = sprintf('git archive --format=zip -o %s %s', escapeshellarg($tmpFile), escapeshellarg($targetDir));
exec("$command 2>&1", $output);
if ($output) {
Json::die([
'result' => false,
'reason' => join(', ', $output)
]);
}
$zip = new \ZipArchive;
if ($zip->open($tmpFile) !== true) {
Json::die([
'result' => false,
'reason' => 'archive가 생성되지 않았습니다.'
]);
}
if (!$zip->extractTo($server)) {
Json::die([
'result' => false,
'reason' => '생성한 archive를 제대로 옮기지 못했습니다.'
]);
}
$zip->close();
@unlink($tmpFile);
$version = getVersion($target);
$result = Util::generateFileUsingSimpleTemplate(
__DIR__ . '/' . $server . '/d_setting/VersionGit.orig.php',
__DIR__ . '/' . $server . '/d_setting/VersionGit.php',
[
'verionGit' => $version
],
true
);
$storage->$server = [$target, $version];
//ServConfig::getServerList()[$server]->closeServer();
opcache_reset();
Json::die([
'server' => $server,
'result' => true,
'version' => $version
]);
+69 -79
View File
@@ -1,79 +1,69 @@
<?php
namespace sammo;
class AppConf
{
private static $serverList = null;
/** @var string 전용 아이콘 경로 */
public static $userIconPath = 'd_pic';
/**
* 서버 설정 반환
*
* @return \sammo\Setting[]
*/
public static function getList()
{
if (self::$serverList === null) {
self::$serverList = [
'che'=>new Setting(ROOT.'/che', '체', 'white'),
'kwe'=>new Setting(ROOT.'/kwe', '퀘', 'yellow'),
'pwe'=>new Setting(ROOT.'/pwe', '풰', 'orange'),
'twe'=>new Setting(ROOT.'/twe', '퉤', 'magenta'),
'nya'=>new Setting(ROOT.'/nya', '냐', '#e67e22'),
'pya'=>new Setting(ROOT.'/pya', '퍄', '#9b59b6'),
'hwe'=>new Setting(ROOT.'/hwe', '훼', 'red')
];
}
return self::$serverList;
}
/**
* DB 객체 생성
*
* @return \MeekroDB
*/
public static function requireRootDB()
{
if (!class_exists('\\sammo\\RootDB')) {
if(!trigger_error('RootDB.php가 설정되지 않았습니다.', E_USER_ERROR)){
die();
}
}
return RootDB::db();
}
/**
* DB 객체 생성
*
* @return \MeekroDB
*/
public static function requireDB()
{
if (!class_exists('\\sammo\\DB')) {
if(!trigger_error('DB.php가 설정되지 않았습니다.', E_USER_ERROR)){
die();
}
}
return DB::db();
}
public static function getUserIconPathFS(string $filepath='') : string{
$path = ROOT.'/'.static::$userIconPath;
if($filepath){
$path .= '/'.$filepath;
}
return $path;
}
public static function getUserIconPathWeb(string $filepath='') : string{
$path = ServConfig::$serverWebPath.'/'.static::$userIconPath;
if($filepath){
$path .= '/'.$filepath;
}
return $path;
}
}
<?php
namespace sammo;
class AppConf
{
private static $serverList = null;
/** @var string 전용 아이콘 경로 */
public static $userIconPath = 'd_pic';
/**
* 서버 설정 반환
*
* @deprecated
* @return \sammo\Setting[]
*/
public static function getList()
{
return ServConfig::getServerList();
}
/**
* DB 객체 생성
*
* @return \MeekroDB
*/
public static function requireRootDB()
{
if (!class_exists('\\sammo\\RootDB')) {
if(!trigger_error('RootDB.php가 설정되지 않았습니다.', E_USER_ERROR)){
die();
}
}
return RootDB::db();
}
/**
* DB 객체 생성
*
* @return \MeekroDB
*/
public static function requireDB()
{
if (!class_exists('\\sammo\\DB')) {
if(!trigger_error('DB.php가 설정되지 않았습니다.', E_USER_ERROR)){
die();
}
}
return DB::db();
}
public static function getUserIconPathFS(string $filepath='') : string{
$path = ROOT.'/'.static::$userIconPath;
if($filepath){
$path .= '/'.$filepath;
}
return $path;
}
public static function getUserIconPathWeb(string $filepath='') : string{
$path = ServConfig::$serverWebPath.'/'.static::$userIconPath;
if($filepath){
$path .= '/'.$filepath;
}
return $path;
}
}
+813 -807
View File
@@ -1,807 +1,813 @@
<?php
namespace sammo;
class Util extends \utilphp\util
{
/**
* int 값 반환을 강제하는 부동소수점 반올림
* @param int|float $value
* @param int $pos 반올림 자리수, 0 이하의 '음수만'
*/
public static function round($value, $pos=0) : int{
assert($pos <= 0, 'Util::round는 음수만 입력 가능');
return intval(round($value, $pos));
}
/**
* int 값으로 강제로 설정하는 부동소수점 반올림
* @param int|float $value
* @param int $pos 반올림 자리수, 0 이하의 '음수만'
*/
public static function setRound(&$value, $pos=0) : void{
$value = static::round($value, $pos);
}
private static function _parseReq($value, string $type)
{
if (is_array($value)) {
if ($type === 'array_int') {
return array_map('intval', $value);
}
if ($type === 'array_string') {
return array_map(function ($item) {
return (string)$item;
}, $value);
}
if ($type === 'array') {
return $value;
}
throw new \InvalidArgumentException('지원할 수 없는 type 지정. array 가 붙은 type이어야 합니다');
}
if ($type === 'bool') {
$value = strtolower($value);
if ($value === null || $value === '' || $value === 'off' || $value === 'false' || $value === 'no' || $value === 'n' || $value === 'x' || $value === 'null') {
return false;
}
return !!$value;
}
if ($type === 'int') {
return (int)$value;
}
if ($type === 'float') {
return (float)$value;
}
if ($type === 'string') {
return (string)$value;
}
throw new \InvalidArgumentException('올바르지 않은 type 지정');
}
public static function zip(\Generator ...$iterators){
while(true){
$hasValue = false;
$values = [];
foreach($iterators as $iter){
if($iter->valid()){
$values[] = $iter->send(NULL);
$hasValue = true;
}
}
yield $values;
if(!$hasValue){
break;
}
}
}
/**
* $_POST, $_GET에서 값을 가져오는 함수. Util::array_get($_POST[$name])을 축약 가능.
* 타입이 복잡해질 경우 이 함수를 통하지 않고 json으로 요청할 것을 권장.
*
* @param string $name 가져오고자 하는 key 이름.
* @param string $type 가져오고자 하는 type. [string, int, float, bool, array, array_string, array_int]
* @param mixed $ifNotExists 만약 $_POST와 $_GET에 값이 없을 경우 반환하는 변수. 이 값은 $type을 검사하지 않음.
* @return int|float|string|array|null
* @throws \InvalidArgumentException
*/
public static function getReq(string $name, string $type = 'string', $ifNotExists = null)
{
if (isset($_POST[$name])) {
$value = $_POST[$name];
} elseif (isset($_GET[$name])) {
$value = $_GET[$name];
} else {
return $ifNotExists;
}
return static::_parseReq($value, $type);
}
/**
* $_POST에서 값을 가져오는 함수. Util::array_get($_POST[$name])을 축약 가능. $_GET에서도 가져올 수 있다면 getReq 사용.
* 타입이 복잡해질 경우 이 함수를 통하지 않고 json으로 요청할 것을 권장.
*
* @param string $name 가져오고자 하는 key 이름.
* @param string $type 가져오고자 하는 type. [string, int, float, bool, array, array_string, array_int]
* @param mixed $ifNotExists 만약 $_GET과 $_POST에 값이 없을 경우 반환하는 변수. 이 값은 $type을 검사하지 않음.
* @return int|float|string|array|null
* @throws \InvalidArgumentException
*/
public static function getPost(string $name, string $type = 'string', $ifNotExists = null)
{
if (isset($_POST[$name])) {
$value = $_POST[$name];
} else {
return $ifNotExists;
}
return static::_parseReq($value, $type);
}
public static function hashPassword($salt, $password)
{
return hash('sha512', $salt.$password.$salt);
}
/**
* 변환할 내용이 _tK_$key_ 형태로 작성된 단순한 템플릿 파일을 이용하여 결과물을 생성해주는 함수.
*/
public static function generateFileUsingSimpleTemplate(string $srcFilePath, string $destFilePath, array $params, bool $canOverwrite=false)
{
if ($destFilePath === $srcFilePath) {
return 'invalid destFilePath';
}
if (!file_exists($srcFilePath)) {
return 'srcFilePath is not exists';
}
if (file_exists($destFilePath) && !$canOverwrite) {
return 'destFilePath is already exists';
}
if (!is_writable(dirname($destFilePath))) {
return 'destFilePath is not writable';
}
$text = file_get_contents($srcFilePath);
foreach ($params as $key => $value) {
$text = str_replace("_tK_{$key}_", $value, $text);
}
file_put_contents($destFilePath, $text);
return true;
}
/**
* params에 맞도록 class를 생성해주는 함수
*/
public static function generatePHPClassFile(string $destFilePath, array $params, ?string $srcClassName=null, string $namespace='sammo'){
if (!is_writable(dirname($destFilePath))) {
return 'destFilePath is not writable';
}
$newClassName = basename($destFilePath, '.php');
$newClassName = basename($newClassName, '.orig');
$head = [];
$head[] = '<?php';
$head[] = "namespace $namespace;";
if($srcClassName === null){
$head[] = "class $newClassName";
}
else{
$head[] = "class $newClassName extends $srcClassName";
}
$head[] = '{';
$head[] = '';
$head = join("\n", $head);
$body = [];
foreach($params as $key=>$value){
if(!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/',$key)){
return "$key is not valid variable name";
}
$body[] = ' public static $'.$key.' = '.var_export($value, true).';';
}
$tail = "\n}";
if(file_exists($destFilePath)){
unlink($destFilePath);
}
$result = file_put_contents($destFilePath, $head.join("\n", $body).$tail, LOCK_EX);
assert($result);
return true;
}
/**
* '비교적' 안전한 int 변환
* null -> null
* int -> int
* float -> int
* numeric(int, float) 포함 -> int
* 기타 -> 예외처리
*
* @return int|null
*/
public static function toInt($val, $silent=false)
{
if (!isset($val)) {
return null;
}
if ($val === null) {
return null;
}
if (is_int($val)) {
return $val;
}
if (is_numeric($val)) {
return intval($val);//
}
if (strtolower($val) === 'null') {
return null;
}
if ($silent) {
if ($val == null) {
return null;
}
if ($val == ''){
return null;
}
return intval($val);
}
throw new \InvalidArgumentException('올바르지 않은 타입형 :'.$val);
}
/**
* Generate a random string, using a cryptographically secure
* pseudorandom number generator (random_int)
*
* For PHP 7, random_int is a PHP core function
* For PHP 5.x, depends on https://github.com/paragonie/random_compat
*
* @param int $length How many characters do we want?
* @param string $keyspace A string of all possible characters
* to select from
* @return string
*/
public static function randomStr($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
{
$str = '';
$max = mb_strlen($keyspace, '8bit') - 1;
for ($i = 0; $i < $length; ++$i) {
$str .= $keyspace[random_int(0, $max)];
}
return $str;
}
public static function mapWithKey($callback, $dict)
{
$result = [];
foreach ($dict as $key=>$value) {
$result[$key] = ($callback)($key, $value);
}
return $result;
}
public static function convertArrayToDict($arr, $keyName)
{
$result = [];
foreach ($arr as $obj) {
$key = $obj[$keyName];
$result[$key] = $obj;
}
return $result;
}
public static function convertArrayToSetLike($arr, $valueIsKey=true){
$result = [];
foreach($arr as $datum){
$result[$datum] = $valueIsKey?$datum:1;
}
return $result;
}
public static function convertPairArrayToDict($arr){
$result = [];
foreach($arr as [$key, $val]){
$result[$key] = $val;
}
return $result;
}
public static function convertDictToArray($dict, bool $withKey=true)
{
$result = [];
foreach($dict as $key=>$value){
if($withKey){
$result[] = [$key, $value];
}
else{
$result[] = $value;
}
}
return $result;
}
public static function squeezeFromArray(array $dict, $key){
$result = [];
foreach($dict as $dictKey=>$value){
$result[$dictKey] = $value[$key];
}
return $result;
}
public static function isDict($array)
{
if($array === null){
return false;
}
if (!is_array($array)) {
//배열이 아니면 dictionary 조차 아님.
return false;
}
if(count($array) === 0){
return true;
}
$idx = 0;
foreach (array_keys($array) as $key) {
if (is_string($key)) {
return true;
}
if($idx !== $key){
return true;
}
$idx = $key + 1;
}
return false;
}
/**
* @param null|mixed|mixed[] $dict
* @return null|mixed|mixed[]
*/
public static function eraseNullValue($dict, int $depth=512)
{
//TODO:Test 추가
if ($dict === null) {
return null;
}
if (is_array($dict) && empty($dict)) {
return null;
}
if ($depth <= 0) {
return $dict;
}
foreach ($dict as $key=>$value) {
if ($value === null) {
unset($dict[$key]);
continue;
}
if (!Util::isDict($value)) {
continue;
}
$newValue = Util::eraseNullValue($value, $depth - 1);
if ($newValue === null) {
unset($dict[$key]);
} else {
$dict[$key] = $newValue;
}
}
return $dict;
}
/**
* key=>value pair를 보존한 섞기
*/
public static function shuffle_assoc(&$array) {
$keys = array_keys($array);
shuffle($keys);
$new = [];
foreach($keys as $key) {
$new[$key] = $array[$key];
}
$array = $new;
return true;
}
/**
* [0.0, 1.0] 사이의 선형 랜덤 float
* @return float
*/
public static function randF()
{
return mt_rand() / mt_getrandmax();
}
/**
* [min, max] 사이의 선형 랜덤 float
* @return float
*/
public static function randRange(float $min, float $max)
{
return static::randF()*($max - $min) + $min;
}
/**
* [min, max] 사이의 선형 랜덤 int
* 현재는 rand(min, max)와 동일
* @return int
*/
public static function randRangeInt(int $min, int $max){
return mt_rand($min, $max);
}
/**
* $prob의 확률로 true를 반환
* @return boolean
*/
public static function randBool($prob = 0.5)
{
return self::randF() < $prob;
}
/**
* aaa(.bbb)?% 의 텍스트를 float으로 변환. 100% = 1.0
* @return float|null
*/
public static function convPercentStrToFloat(string $text):?float{
preg_match('/^(\d+(\.\d+)?)\%$/', $text, $matches);
if($matches === null){
return null;
}
return (float)$matches[1] / 100;
}
/**
* $min과 $max 사이의 값으로 교정
*/
public static function valueFit($value, $min = null, $max = null)
{
if($max !== null && $min !== null && $max < $min){
return $min;
}
if ($min !== null && $value < $min) {
return $min;
}
if ($max !== null && $value > $max) {
return $max;
}
return $value;
}
/**
* 각 값의 비중에 따라 랜덤한 값을 선택
*
* @param array $items 각 수치의 비중
*
* @return int|string 선택된 랜덤 값의 key값. 단순 배열인 경우에는 index
*/
public static function choiceRandomUsingWeight(array $items)
{
$sum = 0;
foreach ($items as $value) {
if($value <= 0){
continue;
}
$sum += $value;
}
$rd = self::randF()*$sum;
foreach ($items as $key=>$value) {
if($value <= 0){
$value = 0;
}
if ($rd <= $value) {
return $key;
}
$rd -= $value;
}
//fallback. 이곳으로 빠지지 않음
end($items);
return key($items);
}
/**
* 각 값의 비중에 따라 랜덤한 값을 선택.
*
* @param array $items 각 수치와 비중. [값, weight] 으로 보관
*
* @return object 선택된 랜덤 값의 첫번째 값
*/
public static function choiceRandomUsingWeightPair(array $items)
{
$sum = 0;
foreach ($items as [$item, $value]) {
if($value <= 0){
continue;
}
$sum += $value;
}
$rd = self::randF()*$sum;
foreach ($items as [$item, $value]) {
if($value <= 0){
$value = 0;
}
if ($rd <= $value) {
return $item;
}
$rd -= $value;
}
//fallback. 이곳으로 빠지지 않음
end($items);
return $items[key($items)][0];
}
/**
* 2중 배열에서 특정 키의 합
*
* @param array $array 배열. 1차원 배열 또는 2차원 배열
* @param int|string|null $key 2차원 배열에서 참조할 키.
* @return int|float 합계
*/
public static function arraySum(array $array, $key = null){
if($key === null){
return array_sum($array);
}
$sum = 0;
foreach($array as $val){
$sum += $val[$key];
}
return $sum;
}
/**
* 특정 키를 가진 값으로 묶음
*
* @param array $array 배열. 1차원 배열 또는 2차원 배열
* @param int|string|null $key 2차원 배열에서 참조할 키.
* @return array
*/
public static function arrayGroupBy(array $array, $key, bool $preserveRowKey=false) {
$result = array();
if($preserveRowKey){
foreach($array as $rowKey=>$val) {
if(key_exists($key, $val)){
$result[$val[$key]][$rowKey] = $val;
}else{
$result[""][$rowKey] = $val;
}
}
}
else{
foreach($array as $val) {
if(key_exists($key, $val)){
$result[$val[$key]][] = $val;
}else{
$result[""][] = $val;
}
}
}
return $result;
}
public static function getKeyOfMaxValue(array $array){
$max = null;
$result = null;
foreach ($array as $key => $value) {
if ($max === null || $value > $max) {
$result = $key;
$max = $value;
}
}
return $result;
}
/**
* 배열의 아무거나 고름. Python의 random.choice()
*
* @param array $items 선택하고자 하는 배열
*
* @return int|float|string|object 선택된 value값.
*/
public static function choiceRandom(array $items)
{
return $items[array_rand($items)];
}
/**
* fqn 클래스 경로에서 클래스 이름을 받아옴
*/
public static function getClassName(string $classpath)
{
if ($pos = strrpos($classpath, '\\')) return substr($classpath, $pos + 1);
return $pos;
}
public static function getClassNameFromObj($object){
$reflect = new \ReflectionClass($object);
return $reflect->getShortName();
}
/**
* 배열의 원소에 대해서 테스트를 수행하고 모두 true인지 확인
*/
public static function testArrayValues(array $array, ?callable $callback):bool{
if($callback === null){
foreach($array as $value){
if(!$value){
return false;
}
}
return true;
}
foreach($array as $value){
if(!($callback)($value)){
return false;
}
}
return true;
}
/**
* MeekroDB에서 %lb의 처리가 이상하여 따로 만든 코드
*/
public static function formatListOfBackticks(array $array):string{
if(!$array){
throw new MustNotBeReachedException('backtick 목록에 없음');
}
//.이 들어간 경우에는 분리해서 묶어야함.
return join(',', array_map(function($value){
$value = preg_replace('/\s/', '', $value);
if(strpos($value, '.') !== false){
$value = explode('.', $value);
$value = join('`.`', $value);
}
return "`{$value}`";
}, $array));
}
public static function joinYearMonth(int $year, int $month):int{
return $year * 12 + $month - 1;
}
public static function parseYearMonth(int $yearMonth):array{
return [intdiv($yearMonth, 12), $yearMonth%12 + 1];
}
/**
* 변수의 값을 첫번째 값부터 비교해서 대소를 반환
* 길이가 다른 경우, 앞의 결과를 먼저 비교한 뒤, 짧은쪽의 값을 null으로 가정하여 길이 처리
*/
public static function arrayCompare(iterable $lhs, iterable $rhs, ?callable $comp=null){
if($lhs instanceof \Traversable){
$lhsIter = new \IteratorIterator($lhs);
}
else if(is_array($lhs)){
$lhsIter = new \ArrayIterator($lhs);
}
else{
throw new \InvalidArgumentException('$lhs is not Traversable');
}
if($rhs instanceof \Traversable){
$rhsIter = new \IteratorIterator($rhs);
}
else if(is_array($rhs)){
$rhsIter = new \ArrayIterator($rhs);
}
else{
throw new \InvalidArgumentException('$rhs is not Traversable');
}
while($lhsIter->valid() && $rhsIter->valid()){
$lhsVal = $lhsIter->current();
$rhsVal = $rhsIter->current();
if($comp !== null){
$compResult = $comp($lhsVal, $rhsVal);
}
else{
$compResult = $lhsVal <=> $rhsVal;
}
if($compResult !== 0){
return $compResult;
}
$lhsIter->next();
$rhsIter->next();
}
$rhsVal = null;
while($lhsIter->valid()){
$lhsVal = $lhsIter->current();
if($comp !== null){
$compResult = $comp($lhsVal, $rhsVal);
}
else{
$compResult = $lhsVal <=> $rhsVal;
}
if($compResult !== 0){
return $compResult;
}
$lhsIter->next();
}
$lhsVal = null;
while($rhsIter->valid()){
$rhsVal = $rhsIter->current();
if($comp !== null){
$compResult = $comp($lhsVal, $rhsVal);
}
else{
$compResult = $lhsVal <=> $rhsVal;
}
if($compResult !== 0){
return $compResult;
}
$rhsIter->next();
}
return 0;
}
public static function isPowerOfTwo(int $number):bool{
if($number <= 0){
return false;
}
return ($number & ($number - 1)) == 0;
}
/**
* Python 3의 range와 동일
* @param int $from
* @param null|int $to
* @param null|int $step
* @return \Traversable
* @throws \InvalidArgumentException
*/
public static function range(int $from, ?int $to=null, ?int $step=null):\Traversable{
if($to === null){
$to = $from;
$from = 0;
}
if($step === null){
$step = 1;
}
else if($step === 0){
throw new \InvalidArgumentException('xrange() arg 3 must not be zero');
}
if($step > 0){
while($from < $to){
yield $from;
$from += $step;
}
}
else{
while($from > $to){
yield $from;
$from += $step;
}
}
}
};
<?php
namespace sammo;
class Util extends \utilphp\util
{
/**
* int 값 반환을 강제하는 부동소수점 반올림
* @param int|float $value
* @param int $pos 반올림 자리수, 0 이하의 '음수만'
*/
public static function round($value, $pos=0) : int{
assert($pos <= 0, 'Util::round는 음수만 입력 가능');
return intval(round($value, $pos));
}
/**
* int 값으로 강제로 설정하는 부동소수점 반올림
* @param int|float $value
* @param int $pos 반올림 자리수, 0 이하의 '음수만'
*/
public static function setRound(&$value, $pos=0) : void{
$value = static::round($value, $pos);
}
private static function _parseReq($value, string $type)
{
if (is_array($value)) {
if ($type === 'array_int') {
return array_map('intval', $value);
}
if ($type === 'array_string') {
return array_map(function ($item) {
return (string)$item;
}, $value);
}
if ($type === 'array') {
return $value;
}
throw new \InvalidArgumentException('지원할 수 없는 type 지정. array 가 붙은 type이어야 합니다');
}
if ($type === 'bool') {
$value = strtolower($value);
if ($value === null || $value === '' || $value === 'off' || $value === 'false' || $value === 'no' || $value === 'n' || $value === 'x' || $value === 'null') {
return false;
}
return !!$value;
}
if ($type === 'int') {
return (int)$value;
}
if ($type === 'float') {
return (float)$value;
}
if ($type === 'string') {
return (string)$value;
}
throw new \InvalidArgumentException('올바르지 않은 type 지정');
}
public static function zip(\Generator ...$iterators){
while(true){
$hasValue = false;
$values = [];
foreach($iterators as $iter){
if($iter->valid()){
$values[] = $iter->send(NULL);
$hasValue = true;
}
}
yield $values;
if(!$hasValue){
break;
}
}
}
/**
* $_POST, $_GET에서 값을 가져오는 함수. Util::array_get($_POST[$name])을 축약 가능.
* 타입이 복잡해질 경우 이 함수를 통하지 않고 json으로 요청할 것을 권장.
*
* @param string $name 가져오고자 하는 key 이름.
* @param string $type 가져오고자 하는 type. [string, int, float, bool, array, array_string, array_int]
* @param mixed $ifNotExists 만약 $_POST와 $_GET에 값이 없을 경우 반환하는 변수. 이 값은 $type을 검사하지 않음.
* @return int|float|string|array|null
* @throws \InvalidArgumentException
*/
public static function getReq(string $name, string $type = 'string', $ifNotExists = null)
{
if (isset($_POST[$name])) {
$value = $_POST[$name];
} elseif (isset($_GET[$name])) {
$value = $_GET[$name];
} else {
return $ifNotExists;
}
return static::_parseReq($value, $type);
}
/**
* $_POST에서 값을 가져오는 함수. Util::array_get($_POST[$name])을 축약 가능. $_GET에서도 가져올 수 있다면 getReq 사용.
* 타입이 복잡해질 경우 이 함수를 통하지 않고 json으로 요청할 것을 권장.
*
* @param string $name 가져오고자 하는 key 이름.
* @param string $type 가져오고자 하는 type. [string, int, float, bool, array, array_string, array_int]
* @param mixed $ifNotExists 만약 $_GET과 $_POST에 값이 없을 경우 반환하는 변수. 이 값은 $type을 검사하지 않음.
* @return int|float|string|array|null
* @throws \InvalidArgumentException
*/
public static function getPost(string $name, string $type = 'string', $ifNotExists = null)
{
if (isset($_POST[$name])) {
$value = $_POST[$name];
} else {
return $ifNotExists;
}
return static::_parseReq($value, $type);
}
public static function hashPassword($salt, $password)
{
return hash('sha512', $salt.$password.$salt);
}
/**
* 변환할 내용이 _tK_$key_ 형태로 작성된 단순한 템플릿 파일을 이용하여 결과물을 생성해주는 함수.
*/
public static function generateFileUsingSimpleTemplate(string $srcFilePath, string $destFilePath, array $params, bool $canOverwrite=false)
{
if ($destFilePath === $srcFilePath) {
return 'invalid destFilePath';
}
if (!file_exists($srcFilePath)) {
return 'srcFilePath is not exists';
}
if (file_exists($destFilePath) && !$canOverwrite) {
return 'destFilePath is already exists';
}
if (!is_writable(dirname($destFilePath))) {
return 'destFilePath is not writable';
}
$text = file_get_contents($srcFilePath);
foreach ($params as $key => $value) {
if(is_array($value)){
$text = str_replace("[/*_tK_{$key}_*/]", var_export($value, true), $text);
}
else{
$text = str_replace("_tK_{$key}_", $value, $text);
}
}
file_put_contents($destFilePath, $text);
return true;
}
/**
* params에 맞도록 class를 생성해주는 함수
*/
public static function generatePHPClassFile(string $destFilePath, array $params, ?string $srcClassName=null, string $namespace='sammo'){
if (!is_writable(dirname($destFilePath))) {
return 'destFilePath is not writable';
}
$newClassName = basename($destFilePath, '.php');
$newClassName = basename($newClassName, '.orig');
$head = [];
$head[] = '<?php';
$head[] = "namespace $namespace;";
if($srcClassName === null){
$head[] = "class $newClassName";
}
else{
$head[] = "class $newClassName extends $srcClassName";
}
$head[] = '{';
$head[] = '';
$head = join("\n", $head);
$body = [];
foreach($params as $key=>$value){
if(!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/',$key)){
return "$key is not valid variable name";
}
$body[] = ' public static $'.$key.' = '.var_export($value, true).';';
}
$tail = "\n}";
if(file_exists($destFilePath)){
unlink($destFilePath);
}
$result = file_put_contents($destFilePath, $head.join("\n", $body).$tail, LOCK_EX);
assert($result);
return true;
}
/**
* '비교적' 안전한 int 변환
* null -> null
* int -> int
* float -> int
* numeric(int, float) 포함 -> int
* 기타 -> 예외처리
*
* @return int|null
*/
public static function toInt($val, $silent=false)
{
if (!isset($val)) {
return null;
}
if ($val === null) {
return null;
}
if (is_int($val)) {
return $val;
}
if (is_numeric($val)) {
return intval($val);//
}
if (strtolower($val) === 'null') {
return null;
}
if ($silent) {
if ($val == null) {
return null;
}
if ($val == ''){
return null;
}
return intval($val);
}
throw new \InvalidArgumentException('올바르지 않은 타입형 :'.$val);
}
/**
* Generate a random string, using a cryptographically secure
* pseudorandom number generator (random_int)
*
* For PHP 7, random_int is a PHP core function
* For PHP 5.x, depends on https://github.com/paragonie/random_compat
*
* @param int $length How many characters do we want?
* @param string $keyspace A string of all possible characters
* to select from
* @return string
*/
public static function randomStr($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
{
$str = '';
$max = mb_strlen($keyspace, '8bit') - 1;
for ($i = 0; $i < $length; ++$i) {
$str .= $keyspace[random_int(0, $max)];
}
return $str;
}
public static function mapWithKey($callback, $dict)
{
$result = [];
foreach ($dict as $key=>$value) {
$result[$key] = ($callback)($key, $value);
}
return $result;
}
public static function convertArrayToDict($arr, $keyName)
{
$result = [];
foreach ($arr as $obj) {
$key = $obj[$keyName];
$result[$key] = $obj;
}
return $result;
}
public static function convertArrayToSetLike($arr, $valueIsKey=true){
$result = [];
foreach($arr as $datum){
$result[$datum] = $valueIsKey?$datum:1;
}
return $result;
}
public static function convertPairArrayToDict($arr){
$result = [];
foreach($arr as [$key, $val]){
$result[$key] = $val;
}
return $result;
}
public static function convertDictToArray($dict, bool $withKey=true)
{
$result = [];
foreach($dict as $key=>$value){
if($withKey){
$result[] = [$key, $value];
}
else{
$result[] = $value;
}
}
return $result;
}
public static function squeezeFromArray(array $dict, $key){
$result = [];
foreach($dict as $dictKey=>$value){
$result[$dictKey] = $value[$key];
}
return $result;
}
public static function isDict($array)
{
if($array === null){
return false;
}
if (!is_array($array)) {
//배열이 아니면 dictionary 조차 아님.
return false;
}
if(count($array) === 0){
return true;
}
$idx = 0;
foreach (array_keys($array) as $key) {
if (is_string($key)) {
return true;
}
if($idx !== $key){
return true;
}
$idx = $key + 1;
}
return false;
}
/**
* @param null|mixed|mixed[] $dict
* @return null|mixed|mixed[]
*/
public static function eraseNullValue($dict, int $depth=512)
{
//TODO:Test 추가
if ($dict === null) {
return null;
}
if (is_array($dict) && empty($dict)) {
return null;
}
if ($depth <= 0) {
return $dict;
}
foreach ($dict as $key=>$value) {
if ($value === null) {
unset($dict[$key]);
continue;
}
if (!Util::isDict($value)) {
continue;
}
$newValue = Util::eraseNullValue($value, $depth - 1);
if ($newValue === null) {
unset($dict[$key]);
} else {
$dict[$key] = $newValue;
}
}
return $dict;
}
/**
* key=>value pair를 보존한 섞기
*/
public static function shuffle_assoc(&$array) {
$keys = array_keys($array);
shuffle($keys);
$new = [];
foreach($keys as $key) {
$new[$key] = $array[$key];
}
$array = $new;
return true;
}
/**
* [0.0, 1.0] 사이의 선형 랜덤 float
* @return float
*/
public static function randF()
{
return mt_rand() / mt_getrandmax();
}
/**
* [min, max] 사이의 선형 랜덤 float
* @return float
*/
public static function randRange(float $min, float $max)
{
return static::randF()*($max - $min) + $min;
}
/**
* [min, max] 사이의 선형 랜덤 int
* 현재는 rand(min, max)와 동일
* @return int
*/
public static function randRangeInt(int $min, int $max){
return mt_rand($min, $max);
}
/**
* $prob의 확률로 true를 반환
* @return boolean
*/
public static function randBool($prob = 0.5)
{
return self::randF() < $prob;
}
/**
* aaa(.bbb)?% 의 텍스트를 float으로 변환. 100% = 1.0
* @return float|null
*/
public static function convPercentStrToFloat(string $text):?float{
preg_match('/^(\d+(\.\d+)?)\%$/', $text, $matches);
if($matches === null){
return null;
}
return (float)$matches[1] / 100;
}
/**
* $min과 $max 사이의 값으로 교정
*/
public static function valueFit($value, $min = null, $max = null)
{
if($max !== null && $min !== null && $max < $min){
return $min;
}
if ($min !== null && $value < $min) {
return $min;
}
if ($max !== null && $value > $max) {
return $max;
}
return $value;
}
/**
* 각 값의 비중에 따라 랜덤한 값을 선택
*
* @param array $items 각 수치의 비중
*
* @return int|string 선택된 랜덤 값의 key값. 단순 배열인 경우에는 index
*/
public static function choiceRandomUsingWeight(array $items)
{
$sum = 0;
foreach ($items as $value) {
if($value <= 0){
continue;
}
$sum += $value;
}
$rd = self::randF()*$sum;
foreach ($items as $key=>$value) {
if($value <= 0){
$value = 0;
}
if ($rd <= $value) {
return $key;
}
$rd -= $value;
}
//fallback. 이곳으로 빠지지 않음
end($items);
return key($items);
}
/**
* 각 값의 비중에 따라 랜덤한 값을 선택.
*
* @param array $items 각 수치와 비중. [값, weight] 으로 보관
*
* @return object 선택된 랜덤 값의 첫번째 값
*/
public static function choiceRandomUsingWeightPair(array $items)
{
$sum = 0;
foreach ($items as [$item, $value]) {
if($value <= 0){
continue;
}
$sum += $value;
}
$rd = self::randF()*$sum;
foreach ($items as [$item, $value]) {
if($value <= 0){
$value = 0;
}
if ($rd <= $value) {
return $item;
}
$rd -= $value;
}
//fallback. 이곳으로 빠지지 않음
end($items);
return $items[key($items)][0];
}
/**
* 2중 배열에서 특정 키의 합
*
* @param array $array 배열. 1차원 배열 또는 2차원 배열
* @param int|string|null $key 2차원 배열에서 참조할 키.
* @return int|float 합계
*/
public static function arraySum(array $array, $key = null){
if($key === null){
return array_sum($array);
}
$sum = 0;
foreach($array as $val){
$sum += $val[$key];
}
return $sum;
}
/**
* 특정 키를 가진 값으로 묶음
*
* @param array $array 배열. 1차원 배열 또는 2차원 배열
* @param int|string|null $key 2차원 배열에서 참조할 키.
* @return array
*/
public static function arrayGroupBy(array $array, $key, bool $preserveRowKey=false) {
$result = array();
if($preserveRowKey){
foreach($array as $rowKey=>$val) {
if(key_exists($key, $val)){
$result[$val[$key]][$rowKey] = $val;
}else{
$result[""][$rowKey] = $val;
}
}
}
else{
foreach($array as $val) {
if(key_exists($key, $val)){
$result[$val[$key]][] = $val;
}else{
$result[""][] = $val;
}
}
}
return $result;
}
public static function getKeyOfMaxValue(array $array){
$max = null;
$result = null;
foreach ($array as $key => $value) {
if ($max === null || $value > $max) {
$result = $key;
$max = $value;
}
}
return $result;
}
/**
* 배열의 아무거나 고름. Python의 random.choice()
*
* @param array $items 선택하고자 하는 배열
*
* @return int|float|string|object 선택된 value값.
*/
public static function choiceRandom(array $items)
{
return $items[array_rand($items)];
}
/**
* fqn 클래스 경로에서 클래스 이름을 받아옴
*/
public static function getClassName(string $classpath)
{
if ($pos = strrpos($classpath, '\\')) return substr($classpath, $pos + 1);
return $pos;
}
public static function getClassNameFromObj($object){
$reflect = new \ReflectionClass($object);
return $reflect->getShortName();
}
/**
* 배열의 원소에 대해서 테스트를 수행하고 모두 true인지 확인
*/
public static function testArrayValues(array $array, ?callable $callback):bool{
if($callback === null){
foreach($array as $value){
if(!$value){
return false;
}
}
return true;
}
foreach($array as $value){
if(!($callback)($value)){
return false;
}
}
return true;
}
/**
* MeekroDB에서 %lb의 처리가 이상하여 따로 만든 코드
*/
public static function formatListOfBackticks(array $array):string{
if(!$array){
throw new MustNotBeReachedException('backtick 목록에 없음');
}
//.이 들어간 경우에는 분리해서 묶어야함.
return join(',', array_map(function($value){
$value = preg_replace('/\s/', '', $value);
if(strpos($value, '.') !== false){
$value = explode('.', $value);
$value = join('`.`', $value);
}
return "`{$value}`";
}, $array));
}
public static function joinYearMonth(int $year, int $month):int{
return $year * 12 + $month - 1;
}
public static function parseYearMonth(int $yearMonth):array{
return [intdiv($yearMonth, 12), $yearMonth%12 + 1];
}
/**
* 변수의 값을 첫번째 값부터 비교해서 대소를 반환
* 길이가 다른 경우, 앞의 결과를 먼저 비교한 뒤, 짧은쪽의 값을 null으로 가정하여 길이 처리
*/
public static function arrayCompare(iterable $lhs, iterable $rhs, ?callable $comp=null){
if($lhs instanceof \Traversable){
$lhsIter = new \IteratorIterator($lhs);
}
else if(is_array($lhs)){
$lhsIter = new \ArrayIterator($lhs);
}
else{
throw new \InvalidArgumentException('$lhs is not Traversable');
}
if($rhs instanceof \Traversable){
$rhsIter = new \IteratorIterator($rhs);
}
else if(is_array($rhs)){
$rhsIter = new \ArrayIterator($rhs);
}
else{
throw new \InvalidArgumentException('$rhs is not Traversable');
}
while($lhsIter->valid() && $rhsIter->valid()){
$lhsVal = $lhsIter->current();
$rhsVal = $rhsIter->current();
if($comp !== null){
$compResult = $comp($lhsVal, $rhsVal);
}
else{
$compResult = $lhsVal <=> $rhsVal;
}
if($compResult !== 0){
return $compResult;
}
$lhsIter->next();
$rhsIter->next();
}
$rhsVal = null;
while($lhsIter->valid()){
$lhsVal = $lhsIter->current();
if($comp !== null){
$compResult = $comp($lhsVal, $rhsVal);
}
else{
$compResult = $lhsVal <=> $rhsVal;
}
if($compResult !== 0){
return $compResult;
}
$lhsIter->next();
}
$lhsVal = null;
while($rhsIter->valid()){
$rhsVal = $rhsIter->current();
if($comp !== null){
$compResult = $comp($lhsVal, $rhsVal);
}
else{
$compResult = $lhsVal <=> $rhsVal;
}
if($compResult !== 0){
return $compResult;
}
$rhsIter->next();
}
return 0;
}
public static function isPowerOfTwo(int $number):bool{
if($number <= 0){
return false;
}
return ($number & ($number - 1)) == 0;
}
/**
* Python 3의 range와 동일
* @param int $from
* @param null|int $to
* @param null|int $step
* @return \Traversable
* @throws \InvalidArgumentException
*/
public static function range(int $from, ?int $to=null, ?int $step=null):\Traversable{
if($to === null){
$to = $from;
$from = 0;
}
if($step === null){
$step = 1;
}
else if($step === 0){
throw new \InvalidArgumentException('xrange() arg 3 must not be zero');
}
if($step > 0){
while($from < $to){
yield $from;
$from += $step;
}
}
else{
while($from > $to){
yield $from;
$from += $step;
}
}
}
};