feat: reuse gamemanager update logic and enforce monthly billing pricing
Agent-Logs-Url: https://github.com/GameServerPanel/GSP/sessions/f982c3a1-c9ae-4c5b-9fb6-2941d0e5b7c1 Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
This commit is contained in:
parent
daae48d9de
commit
5fae4a2dd5
10 changed files with 212 additions and 355 deletions
|
|
@ -29,19 +29,7 @@ function billing_generate_password(int $bytes = 12): string
|
|||
|
||||
function billing_normalize_duration(string $duration): array
|
||||
{
|
||||
$duration = strtolower(trim($duration));
|
||||
switch ($duration) {
|
||||
case 'day':
|
||||
case 'daily':
|
||||
return ['invoice_duration' => 'day', 'rate_type' => 'daily', 'days' => 1];
|
||||
case 'year':
|
||||
case 'yearly':
|
||||
return ['invoice_duration' => 'year', 'rate_type' => 'yearly', 'days' => 365];
|
||||
case 'month':
|
||||
case 'monthly':
|
||||
default:
|
||||
return ['invoice_duration' => 'month', 'rate_type' => 'monthly', 'days' => 31];
|
||||
}
|
||||
return ['invoice_duration' => 'month', 'rate_type' => 'monthly', 'days' => 31];
|
||||
}
|
||||
|
||||
function billing_money_to_cents(float $amount): int
|
||||
|
|
@ -60,23 +48,17 @@ function billing_rate_from_service(mysqli $db, string $table_prefix, int $servic
|
|||
return 0.0;
|
||||
}
|
||||
|
||||
$stmt = $db->prepare("SELECT price_daily, price_monthly, price_year FROM {$table_prefix}billing_services WHERE service_id = ? LIMIT 1");
|
||||
$stmt = $db->prepare("SELECT price_monthly FROM {$table_prefix}billing_services WHERE service_id = ? LIMIT 1");
|
||||
if (!$stmt) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$stmt->bind_param('i', $service_id);
|
||||
$stmt->execute();
|
||||
$stmt->bind_result($price_daily, $price_monthly, $price_year);
|
||||
$stmt->bind_result($price_monthly);
|
||||
$rate = 0.0;
|
||||
if ($stmt->fetch()) {
|
||||
if ($rate_type === 'daily') {
|
||||
$rate = floatval($price_daily);
|
||||
} elseif ($rate_type === 'yearly') {
|
||||
$rate = floatval($price_year);
|
||||
} else {
|
||||
$rate = floatval($price_monthly);
|
||||
}
|
||||
$rate = floatval($price_monthly);
|
||||
}
|
||||
$stmt->close();
|
||||
|
||||
|
|
@ -179,19 +161,13 @@ $slot_min_qty = 1;
|
|||
$slot_max_qty = 1;
|
||||
$durationInfo = billing_normalize_duration($invoice_duration);
|
||||
if ($service_id > 0) {
|
||||
$stmt = $db->prepare("SELECT service_name, price_daily, price_monthly, price_year, slot_min_qty, slot_max_qty FROM {$table_prefix}billing_services WHERE service_id = ? LIMIT 1");
|
||||
$stmt = $db->prepare("SELECT service_name, price_monthly, slot_min_qty, slot_max_qty FROM {$table_prefix}billing_services WHERE service_id = ? LIMIT 1");
|
||||
if ($stmt) {
|
||||
$stmt->bind_param('i', $service_id);
|
||||
$stmt->execute();
|
||||
$stmt->bind_result($service_name, $price_daily, $price_monthly, $price_year, $slot_min_qty, $slot_max_qty);
|
||||
$stmt->bind_result($service_name, $price_monthly, $slot_min_qty, $slot_max_qty);
|
||||
if ($stmt->fetch()) {
|
||||
if ($durationInfo['rate_type'] === 'daily') {
|
||||
$base_rate = floatval($price_daily);
|
||||
} elseif ($durationInfo['rate_type'] === 'yearly') {
|
||||
$base_rate = floatval($price_year);
|
||||
} else {
|
||||
$base_rate = floatval($price_monthly);
|
||||
}
|
||||
$base_rate = floatval($price_monthly);
|
||||
// constrain slots
|
||||
if ($max_players < $slot_min_qty) $max_players = $slot_min_qty;
|
||||
if ($max_players > $slot_max_qty) $max_players = $slot_max_qty;
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@
|
|||
.muted { color: #999; font-size: 0.85em; }
|
||||
.flash-ok { background: #d4edda; border: 1px solid #c3e6cb; padding: 10px 12px; margin-bottom: 10px; border-radius: 6px; color: #155724; }
|
||||
.flash-err { background: #f8d7da; border: 1px solid #f5c6cb; padding: 10px 12px; margin-bottom: 10px; border-radius: 6px; color: #721c24; }
|
||||
.servers-cell { text-align: left; }
|
||||
.server-cb-label { display: block; white-space: nowrap; margin: 2px 0; }
|
||||
.servers-cell { text-align: left; min-width: 240px; max-width: 280px; }
|
||||
.server-cb-label { display: block; white-space: normal; margin: 2px 0; }
|
||||
.action-cell { text-align: center; min-width: 120px; }
|
||||
.btn-row-save, .btn-save-all {
|
||||
border: 1px solid #3e7ab8;
|
||||
|
|
@ -65,7 +65,7 @@
|
|||
*
|
||||
* Columns that are admin-editable and NEVER overwritten by sync:
|
||||
* enabled, slot_min_qty, slot_max_qty,
|
||||
* price_daily, price_monthly, price_year,
|
||||
* price_monthly,
|
||||
* remote_server_id, description, img_url
|
||||
*/
|
||||
|
||||
|
|
@ -310,7 +310,7 @@ $flashType = 'ok';
|
|||
$sort = strtolower((string)($_GET['sort'] ?? $_POST['sort'] ?? 'game'));
|
||||
$dir = strtolower((string)($_GET['dir'] ?? $_POST['dir'] ?? 'asc')) === 'desc' ? 'desc' : 'asc';
|
||||
$gameMode = strtolower((string)($_GET['game_mode'] ?? $_POST['game_mode'] ?? 'name'));
|
||||
if (!in_array($sort, ['game', 'config', 'enabled', 'day', 'month', 'year', 'servers'], true)) {
|
||||
if (!in_array($sort, ['game', 'config', 'enabled', 'month', 'servers'], true)) {
|
||||
$sort = 'game';
|
||||
}
|
||||
if (!in_array($gameMode, ['name', 'enabled'], true)) {
|
||||
|
|
@ -367,9 +367,7 @@ if (isset($_POST['save_services']) || isset($_POST['save_row'])) {
|
|||
continue;
|
||||
}
|
||||
$enabled = isset($svcData['enabled']) ? 1 : 0;
|
||||
$priceDaily = number_format((float)($svcData['price_daily'] ?? 0), 2, '.', '');
|
||||
$priceMonthly = number_format((float)($svcData['price_monthly'] ?? 0), 2, '.', '');
|
||||
$priceYear = number_format((float)($svcData['price_year'] ?? 0), 2, '.', '');
|
||||
$slotMin = max(1, (int)($svcData['slot_min_qty'] ?? 1));
|
||||
$slotMax = max(1, (int)($svcData['slot_max_qty'] ?? 1));
|
||||
if ($slotMax < $slotMin) { $slotMax = $slotMin; }
|
||||
|
|
@ -396,9 +394,7 @@ if (isset($_POST['save_services']) || isset($_POST['save_row'])) {
|
|||
$ok = $db->query(
|
||||
"UPDATE `{$table_prefix}billing_services`
|
||||
SET enabled = {$enabled},
|
||||
price_daily = '{$priceDaily}',
|
||||
price_monthly = '{$priceMonthly}',
|
||||
price_year = '{$priceYear}',
|
||||
slot_min_qty = {$slotMin},
|
||||
slot_max_qty = {$slotMax},
|
||||
description = '{$description}',
|
||||
|
|
@ -453,7 +449,7 @@ while ($rsRes && ($row = $rsRes->fetch_assoc())) {
|
|||
$services = [];
|
||||
$svcRes = $db->query(
|
||||
"SELECT bs.service_id, bs.service_name, bs.enabled,
|
||||
bs.price_daily, bs.price_monthly, bs.price_year,
|
||||
bs.price_monthly,
|
||||
bs.slot_min_qty, bs.slot_max_qty,
|
||||
bs.remote_server_id, bs.description, bs.img_url,
|
||||
ch.home_cfg_file
|
||||
|
|
@ -474,15 +470,9 @@ if (!empty($services)) {
|
|||
case 'enabled':
|
||||
$cmp = ((int)($a['enabled'] ?? 0)) <=> ((int)($b['enabled'] ?? 0));
|
||||
break;
|
||||
case 'day':
|
||||
$cmp = ((float)($a['price_daily'] ?? 0)) <=> ((float)($b['price_daily'] ?? 0));
|
||||
break;
|
||||
case 'month':
|
||||
$cmp = ((float)($a['price_monthly'] ?? 0)) <=> ((float)($b['price_monthly'] ?? 0));
|
||||
break;
|
||||
case 'year':
|
||||
$cmp = ((float)($a['price_year'] ?? 0)) <=> ((float)($b['price_year'] ?? 0));
|
||||
break;
|
||||
case 'servers':
|
||||
$countA = trim((string)($a['remote_server_id'] ?? '')) === '' ? 0 : count(array_filter(explode(',', (string)$a['remote_server_id']), 'strlen'));
|
||||
$countB = trim((string)($b['remote_server_id'] ?? '')) === '' ? 0 : count(array_filter(explode(',', (string)$b['remote_server_id']), 'strlen'));
|
||||
|
|
@ -549,18 +539,10 @@ if (!empty($services)) {
|
|||
</th>
|
||||
<th>Min Slots</th>
|
||||
<th>Max Slots</th>
|
||||
<th>
|
||||
<?php $p = sort_link_params('day', $sort, $dir, $gameMode); ?>
|
||||
<a class="sort-link <?php echo $sort === 'day' ? 'sort-active' : ''; ?>" href="/adminserverlist.php?<?php echo h(http_build_query($p)); ?>">Price / Day ($)</a>
|
||||
</th>
|
||||
<th>
|
||||
<?php $p = sort_link_params('month', $sort, $dir, $gameMode); ?>
|
||||
<a class="sort-link <?php echo $sort === 'month' ? 'sort-active' : ''; ?>" href="/adminserverlist.php?<?php echo h(http_build_query($p)); ?>">Price / Month ($)</a>
|
||||
</th>
|
||||
<th>
|
||||
<?php $p = sort_link_params('year', $sort, $dir, $gameMode); ?>
|
||||
<a class="sort-link <?php echo $sort === 'year' ? 'sort-active' : ''; ?>" href="/adminserverlist.php?<?php echo h(http_build_query($p)); ?>">Price / Year ($)</a>
|
||||
</th>
|
||||
<th>Description</th>
|
||||
<th>Image</th>
|
||||
<th>
|
||||
|
|
@ -615,24 +597,12 @@ if (!empty($services)) {
|
|||
value="<?php echo (int)$svc['slot_max_qty']; ?>">
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input type="number" step="0.01" min="0" class="price-input"
|
||||
name="svc[<?php echo $sid; ?>][price_daily]"
|
||||
value="<?php echo h(number_format((float)$svc['price_daily'], 2, '.', '')); ?>">
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input type="number" step="0.01" min="0" class="price-input"
|
||||
name="svc[<?php echo $sid; ?>][price_monthly]"
|
||||
value="<?php echo h(number_format((float)$svc['price_monthly'], 2, '.', '')); ?>">
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input type="number" step="0.01" min="0" class="price-input"
|
||||
name="svc[<?php echo $sid; ?>][price_year]"
|
||||
value="<?php echo h(number_format((float)$svc['price_year'], 2, '.', '')); ?>">
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input type="text" class="desc-input"
|
||||
name="svc[<?php echo $sid; ?>][description]"
|
||||
|
|
@ -716,6 +686,7 @@ if (!empty($services)) {
|
|||
<ul>
|
||||
<li>A service will only appear in the store when <strong>Enabled</strong> is checked
|
||||
<em>and</em> at least one server is selected.</li>
|
||||
<li><strong>Price / Month ($)</strong> is the canonical billing price used by cart, checkout, and provisioning.</li>
|
||||
<li>The <strong>Game Name</strong> and <strong>Config XML</strong> columns are sourced
|
||||
from <code><?php echo h("{$table_prefix}config_homes"); ?></code> and are read-only
|
||||
here. To change them, update the game XML config in the panel.</li>
|
||||
|
|
|
|||
|
|
@ -124,19 +124,7 @@ function cap_invoice_ids_from_custom_id(?string $customId): array {
|
|||
}
|
||||
|
||||
function cap_get_duration_metadata(array $invoice): array {
|
||||
$duration = strtolower((string)($invoice['invoice_duration'] ?? $invoice['rate_type'] ?? 'month'));
|
||||
switch ($duration) {
|
||||
case 'day':
|
||||
case 'daily':
|
||||
return ['invoice_duration' => 'day', 'rate_type' => 'daily', 'days' => 1];
|
||||
case 'year':
|
||||
case 'yearly':
|
||||
return ['invoice_duration' => 'year', 'rate_type' => 'yearly', 'days' => 365];
|
||||
case 'month':
|
||||
case 'monthly':
|
||||
default:
|
||||
return ['invoice_duration' => 'month', 'rate_type' => 'monthly', 'days' => 31];
|
||||
}
|
||||
return ['invoice_duration' => 'month', 'rate_type' => 'monthly', 'days' => 31];
|
||||
}
|
||||
|
||||
function cap_get_end_date(array $invoice, ?string $fromDate = null): string {
|
||||
|
|
|
|||
|
|
@ -111,19 +111,7 @@ require_once __DIR__ . '/classes/BillingService.php';
|
|||
$repo = new BillingRepository($mysqli, $table_prefix);
|
||||
$newOrderIds = [];
|
||||
$duration_meta = static function (array $invoice): array {
|
||||
$duration = strtolower((string)($invoice['invoice_duration'] ?? $invoice['rate_type'] ?? 'month'));
|
||||
switch ($duration) {
|
||||
case 'day':
|
||||
case 'daily':
|
||||
return ['invoice_duration' => 'day', 'rate_type' => 'daily', 'days' => 1];
|
||||
case 'year':
|
||||
case 'yearly':
|
||||
return ['invoice_duration' => 'year', 'rate_type' => 'yearly', 'days' => 365];
|
||||
case 'month':
|
||||
case 'monthly':
|
||||
default:
|
||||
return ['invoice_duration' => 'month', 'rate_type' => 'monthly', 'days' => 31];
|
||||
}
|
||||
return ['invoice_duration' => 'month', 'rate_type' => 'monthly', 'days' => 31];
|
||||
};
|
||||
|
||||
foreach ($invoices as $inv) {
|
||||
|
|
|
|||
|
|
@ -33,23 +33,9 @@ class BillingService
|
|||
{
|
||||
$qty = max(1, $qty);
|
||||
$players = max(1, $players);
|
||||
|
||||
switch ($rateType) {
|
||||
case 'daily':
|
||||
$basePrice = (float)($service['price_daily'] ?? 0);
|
||||
$periodDays = $qty;
|
||||
break;
|
||||
case 'yearly':
|
||||
$basePrice = (float)($service['price_year'] ?? 0);
|
||||
$periodDays = $qty * 365;
|
||||
break;
|
||||
case 'monthly':
|
||||
default:
|
||||
$rateType = 'monthly';
|
||||
$basePrice = (float)($service['price_monthly'] ?? 0);
|
||||
$periodDays = $qty * 31;
|
||||
break;
|
||||
}
|
||||
$rateType = 'monthly';
|
||||
$basePrice = (float)($service['price_monthly'] ?? 0);
|
||||
$periodDays = $qty * 31;
|
||||
|
||||
// price_monthly etc is the per-player per-period rate
|
||||
$ratePerPlayer = $basePrice;
|
||||
|
|
@ -163,9 +149,7 @@ class BillingService
|
|||
$periodEnd = $invoiceRow['period_end'] ?? null;
|
||||
|
||||
if (!$periodEnd) {
|
||||
$rateType = $invoiceRow['rate_type'] ?? 'monthly';
|
||||
$periodMap = ['daily' => '+1 day', 'monthly' => '+31 days', 'yearly' => '+365 days'];
|
||||
$periodEnd = date('Y-m-d H:i:s', strtotime($periodMap[$rateType] ?? '+31 days'));
|
||||
$periodEnd = date('Y-m-d H:i:s', strtotime('+31 days'));
|
||||
}
|
||||
|
||||
// If current expiry is in the future, extend from it; otherwise reset from period_end
|
||||
|
|
@ -177,9 +161,7 @@ class BillingService
|
|||
if ($periodStart && $periodEndVal) {
|
||||
$currentPeriodSecs = strtotime($periodEndVal) - strtotime($periodStart);
|
||||
} else {
|
||||
$rateType2 = $invoiceRow['rate_type'] ?? 'monthly';
|
||||
$periodSecMap = ['daily' => 86400, 'monthly' => 31 * 86400, 'yearly' => 365 * 86400];
|
||||
$currentPeriodSecs = $periodSecMap[$rateType2] ?? (31 * 86400);
|
||||
$currentPeriodSecs = 31 * 86400;
|
||||
}
|
||||
$newExpiry = date('Y-m-d H:i:s', strtotime($currentExpiry) + max(86400, $currentPeriodSecs));
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../../includes/lib_remote.php';
|
||||
require_once __DIR__ . '/../config_games/server_config_parser.php';
|
||||
require_once __DIR__ . '/../gamemanager/update_actions.php';
|
||||
|
||||
if (!function_exists('billing_generate_provision_password')) {
|
||||
function billing_generate_provision_password(int $bytes = 12)
|
||||
|
|
@ -389,99 +390,24 @@ function exec_ogp_module()
|
|||
}
|
||||
}
|
||||
|
||||
//Read the Game Config from the XML file
|
||||
if (!$order_failed) {
|
||||
$server_xml = read_server_config(SERVER_CONFIG_LOCATION."/".$home_info['home_cfg_file']);
|
||||
if ($server_xml === false) {
|
||||
$order_failed = true;
|
||||
$order_failure_reason = "Could not read server XML for home #{$home_id}.";
|
||||
}
|
||||
}
|
||||
|
||||
//Get Values from XML
|
||||
$mod_xml = false;
|
||||
$modkey = '';
|
||||
$installer_name = '';
|
||||
if (!$order_failed) {
|
||||
$selected_mod = $home_info['mods'][$mod_id] ?? reset($home_info['mods']);
|
||||
if (empty($selected_mod) || empty($selected_mod['mod_key'])) {
|
||||
$order_failed = true;
|
||||
$order_failure_reason = "No valid mod profile found for home #{$home_id}.";
|
||||
} else {
|
||||
$modkey = (string)$selected_mod['mod_key'];
|
||||
$mod_xml = xml_get_mod($server_xml, $modkey);
|
||||
if ($mod_xml === false && isset($server_xml->mods->mod[0])) {
|
||||
$mod_xml = $server_xml->mods->mod[0];
|
||||
$modkey = (string)$mod_xml['key'];
|
||||
}
|
||||
if ($mod_xml === false) {
|
||||
$order_failed = true;
|
||||
$order_failure_reason = "No installable mod profile exists in XML for home #{$home_id}.";
|
||||
} else {
|
||||
$installer_name = (string)$mod_xml->installer_name;
|
||||
$resolved_mod_cfg_id = intval($selected_mod['mod_cfg_id'] ?? $resolved_mod_cfg_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Get Preinstall commands from xml
|
||||
$precmd = !$order_failed ? $server_xml->pre_install : '';
|
||||
|
||||
|
||||
//Get Postinstall commands from xml
|
||||
$postcmd = !$order_failed ? $server_xml->post_install : '';
|
||||
|
||||
|
||||
//Enable FTP account in remote server
|
||||
if (!$order_failed && $ftp == "enabled")
|
||||
{
|
||||
$remote->ftp_mgr("useradd", $home_info['home_id'], $home_info['ftp_password'], $home_info['home_path']);
|
||||
$db->changeFtpStatus('enabled',$home_info['home_id']);
|
||||
}
|
||||
|
||||
//Install files for this service in the remote server
|
||||
if (!$order_failed) {
|
||||
$exec_folder_path = clean_path($home_info['home_path'] . "/" . $server_xml->exe_location );
|
||||
$exec_path = clean_path($exec_folder_path . "/" . $server_xml->server_exec_name );
|
||||
}
|
||||
|
||||
if (!$order_failed && (string)$server_xml->installer === "steamcmd" && !empty((string)$installer_name) )
|
||||
{
|
||||
if( preg_match("/win32/", $server_xml->game_key) OR preg_match("/win64/", $server_xml->game_key) )
|
||||
$cfg_os = "windows";
|
||||
elseif( preg_match("/linux/", $server_xml->game_key) )
|
||||
$cfg_os = "linux";
|
||||
|
||||
// Some games like L4D2 require anonymous login
|
||||
if(!empty($mod_xml->installer_login)){
|
||||
$login = $mod_xml->installer_login;
|
||||
$pass = '';
|
||||
}else{
|
||||
$login = $settings['steam_user'];
|
||||
$pass = $settings['steam_pass'];
|
||||
}
|
||||
|
||||
$modname = ( $installer_name == '90' and !preg_match("/(cstrike|valve)/", $modkey) ) ? $modkey : '';
|
||||
$betaname = isset($mod_xml->betaname) ? $mod_xml->betaname : '';
|
||||
$betapwd = isset($mod_xml->betapwd) ? $mod_xml->betapwd : '';
|
||||
$arch = isset($mod_xml->steam_bitness) ? $mod_xml->steam_bitness : '';
|
||||
|
||||
$remote->steam_cmd( $home_id,$home_info['home_path'],$installer_name,$modname,
|
||||
$betaname,$betapwd,$login,$pass,$settings['steam_guard'],
|
||||
$exec_folder_path,$exec_path,$precmd,$postcmd,$cfg_os,'',$arch);
|
||||
}
|
||||
elseif (!$order_failed)
|
||||
{
|
||||
// No SteamCMD installer — run pre/post install scripts only.
|
||||
if (!empty((string)$precmd)) {
|
||||
$result = $remote->exec((string)$precmd);
|
||||
if ($result === NULL)
|
||||
$db->logger("Script-only install: pre_install script returned no output for home_id $home_id");
|
||||
}
|
||||
if (!empty((string)$postcmd)) {
|
||||
$result = $remote->exec((string)$postcmd);
|
||||
if ($result === NULL)
|
||||
$db->logger("Script-only install: post_install script returned no output for home_id $home_id");
|
||||
if (!$order_failed) {
|
||||
$autoInstall = gamemanager_trigger_update_install(
|
||||
$db,
|
||||
$home_info,
|
||||
intval($mod_id),
|
||||
array('settings' => $settings)
|
||||
);
|
||||
$mod_id = intval($autoInstall['mod_id'] ?? $mod_id);
|
||||
if (empty($autoInstall['ok'])) {
|
||||
$order_failed = true;
|
||||
$order_failure_reason = "Server files have not been installed yet. " . ($autoInstall['message'] ?? 'Auto install could not be started.');
|
||||
}
|
||||
}
|
||||
if (!$order_failed) {
|
||||
|
|
@ -529,54 +455,18 @@ function exec_ogp_module()
|
|||
}
|
||||
$end_date_str = date('Y-m-d H:i:s', $existing_end);
|
||||
}
|
||||
elseif ($order['invoice_duration'] == "day")
|
||||
else
|
||||
{
|
||||
|
||||
if(empty($order['end_date']) || $order['end_date'] === NULL){
|
||||
$end_date = strtotime('+'.$order['qty'].' day');
|
||||
$qty_days = max(1, intval($order['qty'])) * 31;
|
||||
if (empty($order['end_date']) || $order['end_date'] === NULL) {
|
||||
$end_date = strtotime('+' . $qty_days . ' day');
|
||||
} else {
|
||||
$current_end = strtotime($order['end_date']);
|
||||
if ($current_end === false) {
|
||||
$current_end = time();
|
||||
}
|
||||
$end_date = strtotime('+' . $qty_days . ' day', $current_end);
|
||||
}
|
||||
else{
|
||||
//this is a renewel, start from end of previous order
|
||||
$current_end = strtotime($order['end_date']);
|
||||
if ($current_end === false) {
|
||||
$current_end = time(); // fallback to now if date is invalid
|
||||
}
|
||||
$end_date = strtotime('+'.$order['qty'].' day', $current_end);
|
||||
}
|
||||
|
||||
}
|
||||
elseif ($order['invoice_duration'] == "month")
|
||||
{
|
||||
// this is a new order
|
||||
if(empty($order['end_date']) || $order['end_date'] === NULL){
|
||||
$end_date = strtotime('+'.(intval($order['qty']) * 31).' day');
|
||||
|
||||
}
|
||||
else{
|
||||
//this is a renewel, start from end of previous order
|
||||
$current_end = strtotime($order['end_date']);
|
||||
if ($current_end === false) {
|
||||
$current_end = time(); // fallback to now if date is invalid
|
||||
}
|
||||
$end_date = strtotime('+'.(intval($order['qty']) * 31).' day', $current_end);
|
||||
}
|
||||
}
|
||||
elseif ($order['invoice_duration'] == "year")
|
||||
{
|
||||
// this is a new order
|
||||
if(empty($order['end_date']) || $order['end_date'] === NULL){
|
||||
$end_date = strtotime('+'.$order['qty'].' year');
|
||||
}
|
||||
else{
|
||||
//this is a renewel, start from end of previous order
|
||||
$current_end = strtotime($order['end_date']);
|
||||
if ($current_end === false) {
|
||||
$current_end = time(); // fallback to now if date is invalid
|
||||
}
|
||||
$end_date = strtotime('+'.$order['qty'].' year', $current_end);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if (!isset($end_date_str)) {
|
||||
$end_date_str = date('Y-m-d H:i:s', $end_date);
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ if ($db && $user_id > 0) {
|
|||
<h3>What Happens Next?</h3>
|
||||
<ul>
|
||||
<li><strong>✓ Payment Confirmed:</strong> Your payment has been captured by PayPal</li>
|
||||
<li><strong>⚙️ Server Provisioning:</strong> Your game server(s) will be automatically created when you log into the panel</li>
|
||||
<li><strong>⚙️ Server Provisioning:</strong> Your game server(s) are queued for automatic install now; if a node is unavailable they remain clearly marked as pending install</li>
|
||||
<li><strong>📧 Email Notification:</strong> You'll receive a confirmation email with your order details</li>
|
||||
<li><strong>🎮 Access Your Servers:</strong> Log into the Game Server Panel to manage your new servers</li>
|
||||
</ul>
|
||||
|
|
|
|||
131
modules/gamemanager/update_actions.php
Normal file
131
modules/gamemanager/update_actions.php
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
if (!function_exists('gamemanager_choose_mod_id')) {
|
||||
function gamemanager_choose_mod_id(array $home_info, int $preferred_mod_id = 0): int
|
||||
{
|
||||
$mods = $home_info['mods'] ?? array();
|
||||
if (!is_array($mods) || empty($mods)) {
|
||||
return 0;
|
||||
}
|
||||
if ($preferred_mod_id > 0 && isset($mods[$preferred_mod_id])) {
|
||||
return $preferred_mod_id;
|
||||
}
|
||||
$keys = array_keys($mods);
|
||||
return intval(reset($keys));
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('gamemanager_trigger_update_install')) {
|
||||
function gamemanager_trigger_update_install($db, array $home_info, int $mod_id, array $options = array()): array
|
||||
{
|
||||
$home_id = intval($home_info['home_id'] ?? 0);
|
||||
$mod_id = gamemanager_choose_mod_id($home_info, $mod_id);
|
||||
if ($home_id <= 0) {
|
||||
return array('ok' => false, 'pending' => true, 'message' => 'Invalid home_id.', 'mod_id' => $mod_id);
|
||||
}
|
||||
if ($mod_id <= 0 || empty($home_info['mods'][$mod_id])) {
|
||||
return array('ok' => false, 'pending' => true, 'message' => "No mod profile configured for home #{$home_id}.", 'mod_id' => $mod_id);
|
||||
}
|
||||
|
||||
$server_xml = read_server_config(SERVER_CONFIG_LOCATION . "/" . $home_info['home_cfg_file']);
|
||||
if (!$server_xml) {
|
||||
return array('ok' => false, 'pending' => true, 'message' => "Could not read server config XML for home #{$home_id}.", 'mod_id' => $mod_id);
|
||||
}
|
||||
|
||||
$remote = new OGPRemoteLibrary($home_info['agent_ip'], $home_info['agent_port'], $home_info['encryption_key'], $home_info['timeout']);
|
||||
if ($remote->status_chk() === 0) {
|
||||
return array('ok' => false, 'pending' => true, 'message' => 'Agent is offline.', 'mod_id' => $mod_id);
|
||||
}
|
||||
if ($remote->is_screen_running(OGP_SCREEN_TYPE_HOME, $home_id) == 1) {
|
||||
return array('ok' => false, 'pending' => false, 'message' => 'Server is running and cannot be updated.', 'mod_id' => $mod_id);
|
||||
}
|
||||
|
||||
$log_txt = '';
|
||||
$update_active = $remote->get_log(OGP_SCREEN_TYPE_UPDATE, $home_id, clean_path($home_info['home_path']), $log_txt);
|
||||
if ($update_active == 1) {
|
||||
return array('ok' => true, 'started' => true, 'already_running' => true, 'message' => 'Update already in progress.', 'mod_id' => $mod_id);
|
||||
}
|
||||
|
||||
$modkey = $home_info['mods'][$mod_id]['mod_key'] ?? '';
|
||||
$mod_xml = xml_get_mod($server_xml, $modkey);
|
||||
if (!$mod_xml) {
|
||||
return array('ok' => false, 'pending' => true, 'message' => "Mod key '{$modkey}' not found in XML.", 'mod_id' => $mod_id);
|
||||
}
|
||||
|
||||
$installer_name = isset($mod_xml->installer_name) ? (string)$mod_xml->installer_name : (string)$modkey;
|
||||
$precmd = $home_info['mods'][$mod_id]['precmd'] == ""
|
||||
? ($home_info['mods'][$mod_id]['def_precmd'] == "" ? $server_xml->pre_install : $home_info['mods'][$mod_id]['def_precmd'])
|
||||
: $home_info['mods'][$mod_id]['precmd'];
|
||||
$postcmd = $home_info['mods'][$mod_id]['postcmd'] == ""
|
||||
? ($home_info['mods'][$mod_id]['def_postcmd'] == "" ? $server_xml->post_install : $home_info['mods'][$mod_id]['def_precmd'])
|
||||
: $home_info['mods'][$mod_id]['postcmd'];
|
||||
$exec_folder_path = clean_path($home_info['home_path'] . "/" . $server_xml->exe_location);
|
||||
$exec_path = clean_path($exec_folder_path . "/" . $server_xml->server_exec_name);
|
||||
|
||||
$master_server_home_id = intval($options['master_server_home_id'] ?? 0);
|
||||
if ($master_server_home_id > 0) {
|
||||
if ($db->getMasterServer($home_info['remote_server_id'], $home_info['home_cfg_id']) != $master_server_home_id) {
|
||||
return array('ok' => false, 'pending' => false, 'message' => 'Attempting update from non-master server.', 'mod_id' => $mod_id);
|
||||
}
|
||||
if ($master_server_home_id == $home_id) {
|
||||
return array('ok' => false, 'pending' => false, 'message' => 'Cannot update from own self.', 'mod_id' => $mod_id);
|
||||
}
|
||||
$ms_info = $db->getGameHome($master_server_home_id);
|
||||
$steam_out = $remote->masterServerUpdate($home_id, $home_info['home_path'], $master_server_home_id, $ms_info['home_path'], $exec_folder_path, $exec_path, $precmd, $postcmd);
|
||||
if ($steam_out === 0) {
|
||||
return array('ok' => false, 'pending' => true, 'message' => 'Failed to start master update.', 'mod_id' => $mod_id);
|
||||
}
|
||||
return array('ok' => true, 'started' => true, 'message' => 'Update started.', 'mod_id' => $mod_id);
|
||||
}
|
||||
|
||||
$use_steamcmd = ((string)$server_xml->installer === "steamcmd");
|
||||
if ($use_steamcmd && !empty((string)$installer_name)) {
|
||||
$cfg_os = '';
|
||||
if (preg_match("/win32/", $server_xml->game_key) || preg_match("/win64/", $server_xml->game_key)) {
|
||||
$cfg_os = "windows";
|
||||
} elseif (preg_match("/linux/", $server_xml->game_key)) {
|
||||
$cfg_os = "linux";
|
||||
}
|
||||
|
||||
$settings = is_array($options['settings'] ?? null) ? $options['settings'] : $db->getSettings();
|
||||
if (!empty($mod_xml->installer_login)) {
|
||||
$login = (string)$mod_xml->installer_login;
|
||||
$pass = '';
|
||||
} else {
|
||||
$login = (string)($settings['steam_user'] ?? '');
|
||||
$pass = (string)($settings['steam_pass'] ?? '');
|
||||
}
|
||||
|
||||
$modname = ($installer_name == '90') ? $modkey : '';
|
||||
$betaname = isset($mod_xml->betaname) ? (string)$mod_xml->betaname : '';
|
||||
$betapwd = isset($mod_xml->betapwd) ? (string)$mod_xml->betapwd : '';
|
||||
$arch = isset($mod_xml->steam_bitness) ? (string)$mod_xml->steam_bitness : '';
|
||||
$lockFiles = (isset($server_xml->lock_files) && !empty($server_xml->lock_files)) ? trim((string)$server_xml->lock_files) : "";
|
||||
$steam_out = $remote->steam_cmd($home_id, $home_info['home_path'], $installer_name, $modname,
|
||||
$betaname, $betapwd, $login, $pass, $settings['steam_guard'] ?? '',
|
||||
$exec_folder_path, $exec_path, $precmd, $postcmd, $cfg_os, $lockFiles, $arch);
|
||||
if ($steam_out === 0) {
|
||||
return array('ok' => false, 'pending' => true, 'message' => 'Failed to start SteamCMD update.', 'mod_id' => $mod_id);
|
||||
}
|
||||
return array('ok' => true, 'started' => true, 'message' => 'Update started.', 'mod_id' => $mod_id);
|
||||
}
|
||||
|
||||
$ran_scripts = false;
|
||||
if (!empty((string)$precmd)) {
|
||||
$remote->exec((string)$precmd);
|
||||
$ran_scripts = true;
|
||||
}
|
||||
if (!empty((string)$postcmd)) {
|
||||
$remote->exec((string)$postcmd);
|
||||
$ran_scripts = true;
|
||||
}
|
||||
return array(
|
||||
'ok' => true,
|
||||
'started' => $ran_scripts,
|
||||
'completed' => !$ran_scripts,
|
||||
'message' => $ran_scripts ? 'Script install started.' : 'No installer command was required.',
|
||||
'mod_id' => $mod_id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
require_once("includes/lib_remote.php");
|
||||
require_once("modules/config_games/server_config_parser.php");
|
||||
require_once("modules/gamemanager/update_actions.php");
|
||||
|
||||
function exec_ogp_module() {
|
||||
|
||||
|
|
@ -90,111 +91,27 @@ function exec_ogp_module() {
|
|||
// Start update.
|
||||
else if ($_GET['update'] == 'update' && $update_active != 1)
|
||||
{
|
||||
$installer_name = $modkey;
|
||||
|
||||
if ( isset( $mod_xml->installer_name ) )
|
||||
{
|
||||
$installer_name = $mod_xml->installer_name;
|
||||
$start_result = gamemanager_trigger_update_install(
|
||||
$db,
|
||||
$home_info,
|
||||
intval($mod_id),
|
||||
array(
|
||||
'master_server_home_id' => isset($_REQUEST['master_server_home_id']) ? intval($_REQUEST['master_server_home_id']) : 0,
|
||||
'settings' => $db->getSettings(),
|
||||
)
|
||||
);
|
||||
$mod_id = intval($start_result['mod_id'] ?? $mod_id);
|
||||
if (empty($start_result['ok'])) {
|
||||
print_failure(!empty($start_result['message']) ? $start_result['message'] : get_lang("failed_to_start_steam_update"));
|
||||
return;
|
||||
}
|
||||
|
||||
$precmd = $home_info['mods'][$mod_id]['precmd'] == "" ?
|
||||
( $home_info['mods'][$mod_id]['def_precmd'] == "" ? $server_xml->pre_install :
|
||||
$home_info['mods'][$mod_id]['def_precmd'] ) : $home_info['mods'][$mod_id]['precmd'];
|
||||
|
||||
$postcmd = $home_info['mods'][$mod_id]['postcmd'] == "" ?
|
||||
( $home_info['mods'][$mod_id]['def_postcmd'] == "" ? $server_xml->post_install :
|
||||
$home_info['mods'][$mod_id]['def_precmd'] ) : $home_info['mods'][$mod_id]['postcmd'];
|
||||
|
||||
$exec_folder_path = clean_path($home_info['home_path'] . "/" . $server_xml->exe_location );
|
||||
$exec_path = clean_path($exec_folder_path . "/" . $server_xml->server_exec_name );
|
||||
|
||||
if( isset( $_REQUEST['master_server_home_id'] ) )
|
||||
{
|
||||
$ms_home_id = $_REQUEST['master_server_home_id'];
|
||||
|
||||
if ($db->getMasterServer($home_info['remote_server_id'], $home_info['home_cfg_id']) == $ms_home_id) {
|
||||
if ($ms_home_id !== $home_id) {
|
||||
$ms_info = $db->getGameHome($ms_home_id);
|
||||
$steam_out = $remote->masterServerUpdate( $home_id,$home_info['home_path'],$ms_home_id,$ms_info['home_path'],$exec_folder_path,$exec_path,$precmd,$postcmd );
|
||||
} else {
|
||||
print_failure(get_lang('cannot_update_from_own_self'));
|
||||
$view->refresh('?m=gamemanager&p=game_monitor', 2);
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$db->logger(get_lang_f('update_attempt_from_nonmaster_server', $_SESSION['users_login'], $home_id, $ms_home_id));
|
||||
print_failure(get_lang('attempting_nonmaster_update'));
|
||||
$view->refresh('?m=gamemanager&p=game_monitor', 2);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
elseif ($use_steamcmd && !empty((string)$installer_name))
|
||||
{
|
||||
if( preg_match("/win32/", $server_xml->game_key) OR preg_match("/win64/", $server_xml->game_key) )
|
||||
$cfg_os = "windows";
|
||||
elseif( preg_match("/linux/", $server_xml->game_key) )
|
||||
$cfg_os = "linux";
|
||||
|
||||
$settings = $db->getSettings();
|
||||
|
||||
// Some games like L4D2 require anonymous login
|
||||
if($mod_xml->installer_login){
|
||||
$login = $mod_xml->installer_login;
|
||||
$pass = '';
|
||||
}else{
|
||||
$login = $settings['steam_user'];
|
||||
$pass = $settings['steam_pass'];
|
||||
}
|
||||
|
||||
$modname = ( $installer_name == '90' ) ? $modkey : '';
|
||||
$betaname = isset($mod_xml->betaname) ? $mod_xml->betaname : '';
|
||||
$betapwd = isset($mod_xml->betapwd) ? $mod_xml->betapwd : '';
|
||||
$arch = isset($mod_xml->steam_bitness) ? $mod_xml->steam_bitness : '';
|
||||
|
||||
// Additional files to lock
|
||||
if(isset($server_xml->lock_files) && !empty($server_xml->lock_files)){
|
||||
$lockFiles = trim($server_xml->lock_files);
|
||||
}else{
|
||||
$lockFiles = "";
|
||||
}
|
||||
|
||||
$steam_out = $remote->steam_cmd( $home_id,$home_info['home_path'],$installer_name,$modname,
|
||||
$betaname,$betapwd,$login,$pass,$settings['steam_guard'],
|
||||
$exec_folder_path,$exec_path,$precmd,$postcmd,$cfg_os,$lockFiles,$arch);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No SteamCMD installer — run pre/post install scripts only.
|
||||
$ran_scripts = false;
|
||||
if (!empty((string)$precmd)) {
|
||||
$remote->exec((string)$precmd);
|
||||
$ran_scripts = true;
|
||||
}
|
||||
if (!empty((string)$postcmd)) {
|
||||
$remote->exec((string)$postcmd);
|
||||
$ran_scripts = true;
|
||||
}
|
||||
if ($ran_scripts) {
|
||||
print_success( get_lang("update_started") );
|
||||
} else {
|
||||
print_success( get_lang("update_completed") );
|
||||
}
|
||||
if (!empty($start_result['started'])) {
|
||||
print_success(get_lang("update_started"));
|
||||
} else {
|
||||
print_success(get_lang("update_completed"));
|
||||
$view->refresh("?m=gamemanager&p=game_monitor&home_id=$home_id", 3);
|
||||
return;
|
||||
}
|
||||
|
||||
if( $steam_out === 0 )
|
||||
{
|
||||
print_failure( get_lang("failed_to_start_steam_update") );
|
||||
return;
|
||||
}
|
||||
else if ( $steam_out === 1 )
|
||||
{
|
||||
print_success( get_lang("update_started") );
|
||||
}
|
||||
}
|
||||
// Refresh update page.
|
||||
else
|
||||
|
|
|
|||
|
|
@ -64,16 +64,30 @@ function exec_ogp_module()
|
|||
|
||||
// We must always add the home directory to the fm_cwd so that user
|
||||
// can not go out of the homedir.
|
||||
$path = clean_path($home_cfg['home_path']."/".@$_SESSION['fm_cwd_'.$home_id]);
|
||||
$cwd_session_key = 'fm_cwd_' . $home_id;
|
||||
if (!isset($_SESSION[$cwd_session_key]) || !is_string($_SESSION[$cwd_session_key])) {
|
||||
$_SESSION[$cwd_session_key] = '';
|
||||
}
|
||||
$path = clean_path($home_cfg['home_path']."/".$_SESSION[$cwd_session_key]);
|
||||
if (!$remote->rfile_exists($path))
|
||||
{
|
||||
while(!$remote->rfile_exists($path))
|
||||
{
|
||||
$_SESSION['fm_cwd_'.$home_id] = dirname($_SESSION['fm_cwd_'.$home_id]);
|
||||
$path = clean_path($home_cfg['home_path']."/".@$_SESSION['fm_cwd_'.$home_id]);
|
||||
$current_cwd = isset($_SESSION[$cwd_session_key]) ? (string)$_SESSION[$cwd_session_key] : '';
|
||||
if ($current_cwd === '' || $current_cwd === '.' || $current_cwd === DIRECTORY_SEPARATOR) {
|
||||
print_failure("Server files have not been installed yet.");
|
||||
echo "<table class='center'><tr><td><a href='?m=gamemanager&p=game_monitor&home_id=".$home_cfg['home_id']."'><< ".get_lang('back')."</a></td></tr></table>";
|
||||
return;
|
||||
}
|
||||
$parent_cwd = dirname($current_cwd);
|
||||
if (!is_string($parent_cwd) || $parent_cwd === '.' || $parent_cwd === DIRECTORY_SEPARATOR) {
|
||||
$parent_cwd = '';
|
||||
}
|
||||
$_SESSION[$cwd_session_key] = $parent_cwd;
|
||||
$path = clean_path($home_cfg['home_path']."/".$_SESSION[$cwd_session_key]);
|
||||
if($path == clean_path($home_cfg['home_path']."/"))
|
||||
{
|
||||
print_failure(get_lang_f("dir_not_found",$path));
|
||||
print_failure("Server files have not been installed yet.");
|
||||
echo "<table class='center'><tr><td><a href='?m=gamemanager&p=game_monitor&home_id=".$home_cfg['home_id']."'><< ".get_lang('back')."</a></td></tr></table>";
|
||||
return;
|
||||
}
|
||||
|
|
@ -214,7 +228,7 @@ function exec_ogp_module()
|
|||
{
|
||||
$remote->shell_action('remove_recursive', $files);
|
||||
$files = str_replace('" "','"<br>"',$files);
|
||||
$db->logger( get_lang("remove") . ": ${files}" );
|
||||
$db->logger( get_lang("remove") . ": {$files}" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -349,7 +363,7 @@ function exec_ogp_module()
|
|||
if($items != '')
|
||||
{
|
||||
$retval = $remote->compress_files($items,$path,$archive_name,$archive_type);
|
||||
$archive = clean_path( "${path}/${archive_name}.${archive_type}" );
|
||||
$archive = clean_path( "{$path}/{$archive_name}.{$archive_type}" );
|
||||
if( $retval == 0 )
|
||||
{
|
||||
do{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue