Files
fc/server/web/dev.app/mod/client.php
T
2026-05-23 22:10:14 +08:00

428 lines
17 KiB
PHP

<?php
/*-----------------------------------------------------+
* 客户端相关工具
* @author yeahoo2000@gmail.com
+-----------------------------------------------------*/
class client{
private static $instance = null;
private $mainServer;
private $gamebuilder;
private $platform;
private $logfile = "/var/client.log";
private $logMake = "/var/client_make.log";
private $logBuildClientResources = "/var/build_clientresources.log";
private $logBuildGameResources = "/var/build_gameresources.log";
private function __construct(){
$this->logfile = ROOT.$this->logfile;
$this->logMake = ROOT.$this->logMake;
$this->logBuildGameResources = ROOT.$this->logBuildGameResources;
$this->logBuildClientResources = ROOT.$this->logBuildClientResources;
}
private function checkPlatform(){
if(!App::request()->get()->platform){
throw new ErrorException("缺少必要参数platform");
}
$this->platform = App::request()->get()->platform;
}
private function checkGamebuilder(){
$this->gamebuilder = App::cfg()->servers->main;
if(!$this->gamebuilder){
throw new ErrorException("找不到编译服务器gamebuilder的配置信息");
}
}
private function checkMainServer(){
$this->mainServer = App::cfg()->servers->main;
if(!$this->mainServer){
throw new ErrorException("找不到主服务器main的配置信息");
}
}
// 当前脚本被载入时会自动调用此方法
public static function __awake(){
App::sess()->close();
}
private static function getInstance(){
if(is_null(self::$instance)){
self::$instance = new self();
}
return self::$instance;
}
// 写日志文件
private static function writeLog($msg){
error_log($msg, 3, self::getInstance()->logfile);
}
// 输出一条信息到控制台
private static function info($msg){
$name = App::sess()->username;
$time = date("y/m/d H:i:s", time());
self::writeLog(sprintf("[o0m[INFO][%s %s][0;0m %s", $time, $name, $msg));
}
private static function rsync(){
$self = self::getInstance();
$self->checkMainServer();
$self->checkGamebuilder();
self::info("正在同步资源,可能需要比较长的时间,请耐心等待...\n");
$start = time();
$cmd = "/usr/bin/sudo /usr/bin/rsync -a --times --delete -e 'ssh -p{$self->gamebuilder->ssh_port}' {$self->gamebuilder->ssh_user}@{$self->gamebuilder->ip}:{$self->gamebuilder->project_root}/resources/release/ ".ROOT."/var/res_cache/";
$rtn = App::os()->exec($cmd, $stdout, $stderr);
if(0 != $rtn){
self::writeLog("同步资源时发生错误[$rtn]:\n$stderr\n");
return;
}
$cmd = "/usr/bin/sudo /usr/bin/rsync -a --times -e 'ssh -p{$self->mainServer->ssh_port}' ".ROOT."/var/res_cache/ {$self->mainServer->ssh_user}@{$self->mainServer->ip}:{$self->mainServer->project_root}/resources/release/";
$rtn = App::os()->exec($cmd, $stdout, $stderr);
if(0 != $rtn){
self::writeLog("同步资源时发生错误[$rtn]:\n$stderr\n");
return;
}
self::info("同步资源完成,用时 ".(time() - $start)." 秒...\n");
}
// 读取日志
public static function log(){
$file = App::request()->get()->file;
if(!$file){
throw new ErrorException("缺少必要参数file");
}
$self = self::getInstance();
switch($file){
default: throw new ErrorException("参数错误");
case 'client_make': $file = $self->logMake; break;
case 'game_resources': $file = $self->logBuildGameResources; break;
case 'client_resources': $file = $self->logBuildClientResources; break;
}
$view = App::view();
if(file_exists($file)){
$view->vars->content = Common::formatConsole(file_get_contents($file));
}
$view->render('console');
}
// 获取控制台内容
public static function console(){
$view = App::view();
$view->vars->content = Common::formatConsole(Util::tail(self::getInstance()->logfile));
$view->render('console');
}
// 显示主界面
public static function index(){
$srvs = [];
$view = App::view();
$view->vars->console = App::url('/client/console');
$view->render('client');
}
// 生成数值数据
public static function genData(){
$files = App::request()->get()->files;
if(!$files) return;
self::info("正在生成配置数据数据...\n");
Common::genData($files, self::getInstance()->logfile);
self::info("生成配置数据完毕\n");
}
// 生成地图数据
public static function genMap(){
$self = self::getInstance();
$self->checkGamebuilder();
self::info("正在生成地图数据...\n");
$cmd = "bash {$self->gamebuilder->project_root}/dev.sh gen_map";
$rtn = SSH::exec($self->gamebuilder, $cmd, $stdout, $stderr);
if(0 != $rtn){
self::info("生成地图数据失败[$rtn]:\n$stderr\n");
}
self::writeLog($stdout);
}
// 同步中心城数据
public static function genCity(){
$self = self::getInstance();
$self->checkGamebuilder();
self::info("正在同步中心城数据...\n");
$cmd = "bash {$self->gamebuilder->project_root}/dev.sh cli city";
$rtn = SSH::exec($self->gamebuilder, $cmd, $stdout, $stderr);
if(0 != $rtn){
self::info("同步中心城数据失败[$rtn]:\n$stderr\n");
}
self::writeLog($stdout);
}
// 生成协议
public static function proto(){
$self = self::getInstance();
$self->checkGamebuilder();
self::info("正在打包客户端协议...\n");
$cmd = "bash {$self->gamebuilder->project_root}/dev.sh gen_proto";
$rtn = SSH::exec($self->gamebuilder, $cmd, $stdout, $stderr);
if(0 != $rtn){
self::writeLog("打包客户端协议时发生错误[$rtn]:\n$stderr\n");
return;
}
self::writeLog($stdout);
self::rsync();
}
// 生成APK热更新包
public static function pack_apk_zip(){
$self = self::getInstance();
$self->checkGamebuilder();
self::info("正在打包客户端[APK]热更新包...\n");
$cmd = "bash {$self->gamebuilder->project_root}/dev.sh cli make android skip && bash {$self->gamebuilder->project_root}/dev.sh cli pack android demo zip skip";
$rtn = SSH::exec($self->gamebuilder, $cmd, $stdout, $stderr);
if(0 != $rtn){
self::writeLog("打包客户端[APK]热更新包发生错误[$rtn]:\n$stderr\n");
return;
}
self::writeLog($stdout);
}
// 生成IPA热更新包
public static function pack_ipa_zip(){
$self = self::getInstance();
$self->checkGamebuilder();
self::info("正在打包客户端[IOS]热更新包...\n");
$cmd = "bash {$self->gamebuilder->project_root}/dev.sh cli make ios skip && bash {$self->gamebuilder->project_root}/dev.sh cli pack ios demo zip skip";
$rtn = SSH::exec($self->gamebuilder, $cmd, $stdout, $stderr);
if(0 != $rtn){
self::writeLog("打包客户端[IOS]热更新包发生错误[$rtn]:\n$stderr\n");
return;
}
self::writeLog($stdout);
}
// 生成APK
public static function gen_apk(){
$self = self::getInstance();
$self->checkGamebuilder();
self::info("正在打包客户端[APK]...\n");
$cmd = "bash {$self->gamebuilder->project_root}/dev.sh pack apk release";
$rtn = SSH::exec($self->gamebuilder, $cmd, $stdout, $stderr);
if(0 != $rtn){
self::writeLog("打包客户端[APK]发生错误[$rtn]:\n$stderr\n");
return;
}else if(file_exists("{$self->gamebuilder->project_root}/client_core/apk/game-release-signed.apk") && strstr($stdout, "build succeeded")){
copy("{$self->gamebuilder->project_root}/client_core/apk/game-release-signed.apk", ROOT.'/www/apks/sy_game_'. date('YmdHis',time()).'.apk');
}
self::writeLog($stdout);
}
// 获取所有APK信息
public static function apklist(){
$path = ROOT.'/www/apks';
$files = Common::listfiles($path, 'apk');
$view = App::view();
$view->vars->files = $files;
$view->vars->ftype = 'apk';
$view->render('apklist');
}
// 获取所有IPA信息
public static function ipalist(){
$path = ROOT.'/www/ipas';
$files = Common::listfiles($path, 'ipa');
$view = App::view();
$view->vars->files = $files;
$view->vars->ftype = 'ipa';
$view->render('apklist');
}
// 编译GameResources
public static function buildGameResources(){
$self = self::getInstance();
$self->checkPlatform();
$self->checkGamebuilder();
self::info("正在编译 {$self->platform} 版的GameResources,需要比较长的时间,请耐心等待...\n");
$cmd = "bash {$self->gamebuilder->project_root}/dev.sh res build {$self->platform}";
$start = time();
$rtn = SSH::exec($self->gamebuilder, $cmd, $stdout, $stderr);
if(0 != $rtn){
self::writeLog("编译GameResources时发生错误[$rtn]:\n$stderr\n");
return;
}
self::info("编译 {$self->platform} 版的GameResources完成,用时: ".(time() - $start)." 秒,<a href='javascript:void(0);' onclick=\"Util.winOpen('".App::getBaseUrl()."/client/log?file=game_resources', 960, 640, 'game_resources', true)\">点击此处可查看编译日志</a>\n");
Util::writeFile($self->logBuildGameResources, $stdout);
self::rsync();
}
// 编译ClientResources
public static function buildClientResources(){
$self = self::getInstance();
$self->checkPlatform();
$self->checkGamebuilder();
self::info("正在编译 {$self->platform} 版的ClientResources,需要比较长的时间,请耐心等待...\n");
$cmd = "bash {$self->gamebuilder->project_root}/dev.sh cli res {$self->platform}";
$start = time();
$rtn = SSH::exec($self->gamebuilder, $cmd, $stdout, $stderr);
if(0 != $rtn){
self::writeLog("编译GameResources时发生错误[$rtn]:\n$stderr\n");
return;
}
self::info("编译 {$self->platform} 版的ClientResources完成,用时: ".(time() - $start)." 秒,<a href='javascript:void(0);' onclick=\"Util.winOpen('".App::getBaseUrl()."/client/log?file=client_resources', 960, 640, 'client_resources', true)\">点击此处可查看编译日志</a>\n");
Util::writeFile($self->logBuildClientResources, $stdout);
self::rsync();
}
// 生成版本信息
public static function version(){
$self = self::getInstance();
$self->checkGamebuilder();
$self::info("正在生成版本信息...\n");
$cmd = "bash {$self->gamebuilder->project_root}/dev.sh cli version";
$rtn = SSH::exec($self->gamebuilder, $cmd, $stdout, $stderr);
if(0 != $rtn){
self::writeLog("生成版本信息时发生错误[$rtn]:\n$stderr\n");
return;
}
self::writeLog($stdout);
self::rsync();
}
// 编译客户端
public static function make(){
$self = self::getInstance();
$self->checkPlatform();
$self->checkGamebuilder();
$releaseLocal = App::cfg()->client_pack_path;
if(!$releaseLocal){
self::writeLog("env.php中的没有client_pack_path的配置信息");
return;
}
$rtn = App::git()->repo('client')->exec('log --format=%H%n%ct -n 1', $stdout, $stderr);
if(0 != $rtn){
self::writeLog("查询源码库时发生异常[$rtn]:\n$stderr\n");
return;
}
list($hash, $ct) = explode("\n", $stdout);
$ver = date('vymd_Hi', $ct);
$cmd = "bash {$self->gamebuilder->project_root}/dev.sh cli make {$self->platform} {$ver} {$hash}";
self::info("正在编译 {$self->platform} 客户端,需要比较长的时间,请耐心等待...\n");
$start = time();
$rtn = SSH::exec($self->gamebuilder, $cmd, $stdout, $stderr);
if(0 != $rtn){
self::writeLog("编译客户端时发生错误[$rtn]:\n$stderr\n");
return;
}
self::info("编译 {$self->platform} 客户端完成,用时: ".(time() - $start)." 秒,<a href='javascript:void(0);' onclick=\"Util.winOpen('".App::getBaseUrl()."/client/log?file=client_make', 960, 640, 'client_make', true)\">点击此处可查看编译日志</a>\n");
Util::writeFile($self->logMake, $stdout);
self::info("正在同步客户端,请稍等...\n");
$start = time();
$cmd = "/usr/bin/sudo /usr/bin/rsync -a --times --delete-excluded --include '*.zip' --include '*.apk' --include '*.ipa' --exclude '*' -e 'ssh -p{$self->gamebuilder->ssh_port}' {$self->gamebuilder->ssh_user}@{$self->gamebuilder->ip}:{$self->gamebuilder->project_root}/resources/client_packages/ $releaseLocal/";
$rtn = App::os()->exec($cmd, $stdout, $stderr);
if(0 != $rtn){
self::writeLog("同步客户端时发生错误[$rtn]:\n$stderr\n");
return;
}
self::info("正在同步客户端完成,用时: ".(time() - $start)."\n");
}
// 获取客户端所有模块
public static function getMods(){
$path = App::cfg()->client_mod;
$handle = @opendir($path);
if(!$handle)throw new ErrorException("无法打开模块目录");
$mods = ['all', 'launcher', 'core', 'supports'];
while($file = readdir($handle)){
if($file == '.' || $file == '..') continue;
if(!is_dir($path.'/'.$file)) continue;
$mods[] = $file;
}
sort($mods);
return $mods;
}
// 编译模块
public static function makeMod(){
$mods = App::request()->get()->mods;
if(!$mods) return;
$self = self::getInstance();
$self->checkGamebuilder();
$modstr = $mods;
$mods = explode(',', $mods);
self::info("正在编译客户端模块[{$modstr}],可能需要较长时间,请耐心等待....\n");
$is_err = false;
foreach($mods as $mod){
if(!preg_match("/[0-9a-z_]+/i", $mod)) continue;
$cmd = "bash {$self->gamebuilder->project_root}/dev.sh cli make_mod $mod";
$rtn = SSH::exec($self->gamebuilder, $cmd, $stdout, $stderr);
$str = preg_replace('/(Error:.*|Warning:.*)/', '<font color="red">[\1]</font>', $stdout);
self::info("编译客户端模块[{$mod}]结果信息=> \n{$str}\n");
if(0 != $rtn){
self::writeLog("编译客户端时发生错误[$rtn]:\n$stderr\n");
return;
}
if (preg_match ( "/.*(Error:|Warning:).*/", $str)) {
$is_err = true;
}
}
if($is_err){
App::alert("编译模块有错,请认真检查");
}
self::info("编译客户端模块[{$modstr}]完成...\n");
}
public static function cmd(){
// sleep(10);
$self = self::getInstance();
$self->checkGamebuilder();
$cmdstr = App::request()->get()->cmd;
if(!$cmdstr) return;
if(!preg_match('/^[^\|\$\(\)`&><]+$/is', $cmdstr)) return App::alert("命令[$cmdstr]中包含有特殊字符, 执行失败");
self::info("正在执行命令[./dev.sh $cmdstr],某些命令需要处理比较长的时间...\n");
$cmd = "bash {$self->gamebuilder->project_root}/dev.sh $cmdstr";
$rtn = SSH::exec($self->gamebuilder, $cmd, $stdout, $stderr);
if(0 != $rtn){
self::writeLog("执行命令失败[$rtn]:\n$stderr\n");
}else{
self::writeLog("执行结果:\n$stdout\n$stderr");
}
self::info("执行命令[./dev.sh $cmdstr]完毕\n");
}
public static function update_tosvn(){
$self = self::getInstance();
$self->checkGamebuilder();
self::info("开始执行命令[./dev.sh cli_update]\n");
$cmd = "bash {$self->gamebuilder->project_root}/dev.sh cli_update";
$rtn = SSH::exec($self->gamebuilder, $cmd, $stdout, $stderr);
if(0 != $rtn){
self::writeLog("执行命令失败[$rtn]:\n$stdout $stderr\n");
}else{
self::writeLog("执行结果:\n$stdout\n$stderr");
}
self::info("执行命令[./dev.sh cli_update]完毕\n");
}
}