code
This commit is contained in:
parent
9772b05bcb
commit
64fcae1375
|
|
@ -0,0 +1,4 @@
|
|||
thumbs/assets/*
|
||||
thumbs/avatars/*
|
||||
asset/files/*
|
||||
api/private/config.php
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
*
|
||||
{
|
||||
font-size: 12px;
|
||||
font-family: 'Comic Sans MS', Verdana, Arial, Helvetica, sans-serif;
|
||||
}
|
||||
H1
|
||||
{
|
||||
font-weight: bold;
|
||||
font-size: larger;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
require $_SERVER['DOCUMENT_ROOT']."/api/private/core.php";
|
||||
PageBuilder::BuildHeader();
|
||||
?>
|
||||
Do not run <code>game:HttpGet("http://polygon.pizzaboxer.xyz/XD")</code> in studio
|
||||
please no
|
||||
Plz
|
||||
<?php PageBuilder::BuildFooter();
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("System");
|
||||
|
||||
Users::RequireAdmin();
|
||||
|
||||
function getSetupUsage($clients)
|
||||
{
|
||||
$usage = 0;
|
||||
|
||||
if(is_array($clients))
|
||||
{
|
||||
foreach ($clients as $client)
|
||||
{
|
||||
$usage += getSetupUsage($client);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$usage = System::GetFolderSize("/var/www/pizzaboxer.xyz/setup$clients/", true);
|
||||
}
|
||||
|
||||
return $usage;
|
||||
}
|
||||
|
||||
$roles =
|
||||
[
|
||||
Users::STAFF_MODERATOR => "Moderator",
|
||||
Users::STAFF_ADMINISTRATOR => "Administrator",
|
||||
Users::STAFF_CATALOG => "Catalog Manager"
|
||||
];
|
||||
|
||||
$servermemory = System::GetMemoryUsage();
|
||||
$usersOnline = Users::GetUsersOnline();
|
||||
$pendingRenders = Polygon::GetPendingRenders();
|
||||
$thumbPing = SITE_CONFIG["site"]["thumbserver"] == "RCCService2015" ? Polygon::GetServerPing(2) : Polygon::GetServerPing(1);
|
||||
|
||||
$usage = (object)
|
||||
[
|
||||
"Memory" => (object)
|
||||
[
|
||||
"Total" => System::GetFileSize($servermemory->total),
|
||||
"SytemUsage" => System::GetFileSize($servermemory->total-$servermemory->free),
|
||||
"PHPUsage" => System::GetFileSize(memory_get_usage(true))
|
||||
],
|
||||
|
||||
"Disk" => (object)
|
||||
[
|
||||
"Total" => System::GetFileSize(disk_total_space("/")),
|
||||
"SystemUsage" => System::GetFileSize(disk_total_space("/")-disk_free_space("/")),
|
||||
"PolygonUsage" => System::GetFolderSize("/var/www/pizzaboxer.xyz/polygon/"),
|
||||
"ThumbnailUsage" => System::GetFolderSize("/var/www/pizzaboxer.xyz/polygoncdn/"),
|
||||
"SetupUsage" => System::GetFileSize(getSetupUsage([2009, 2010, 2011, 2012]))
|
||||
]
|
||||
];
|
||||
|
||||
PageBuilder::$Config["title"] = SITE_CONFIG["site"]["name"]." Administration";
|
||||
PageBuilder::BuildHeader();
|
||||
?>
|
||||
|
||||
<!--h1 style="position:absolute;opacity:0.5;font-size:10rem;z-index:10000000">THIS IS NOT <br> MULTAKOS SCREENSHOT LOL</h1-->
|
||||
<h2 class="font-weight-normal"><?=SITE_CONFIG["site"]["name"]?> Administration</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-7 divider-right">
|
||||
<h3 class="pb-2 font-weight-normal">You are <?=vowel($roles[SESSION["user"]["adminlevel"]])?></h3>
|
||||
<div class="row px-3 mb-2">
|
||||
<?php if(Users::IsAdmin([Users::STAFF_MODERATOR, Users::STAFF_ADMINISTRATOR, Users::STAFF_CATALOG])) { ?>
|
||||
<div class="col-md-4 py-2 px-1">
|
||||
<a class="btn btn-outline-danger btn-lg btn-block px-0" href="/admin/moderate-user"><i class="fal fa-gavel"></i> Moderate user</a>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<div class="col-md-4 py-2 px-1">
|
||||
<a class="btn btn-outline-danger btn-lg btn-block px-0" href="/admin/moderate-assets"><i class="fal fa-file-exclamation"></i> Moderate assets</a>
|
||||
</div>
|
||||
<div class="col-md-4 py-2 px-1">
|
||||
<a class="btn btn-outline-primary btn-lg btn-block px-0" href="/admin/render-queue"><i class="fal fa-images"></i> Render queue</a>
|
||||
</div>
|
||||
<?php if(Users::IsAdmin(Users::STAFF_ADMINISTRATOR)) { ?>
|
||||
<div class="col-md-4 py-2 px-1">
|
||||
<a class="btn btn-outline-primary btn-lg btn-block px-0" href="/admin/site-banners"><i class="fal fa-bullhorn"></i> Site banners</a>
|
||||
</div>
|
||||
<div class="col-md-4 py-2 px-1">
|
||||
<a class="btn btn-outline-primary btn-lg btn-block px-0" href="#" onclick="polygon.buildModal({header: 'coming soon', body: 'Sample Text', buttons: [{class:'btn btn-primary px-4', dismiss:true, text:'OK'}]});"><i class="fal fa-rss-square"></i> Newsfeed</a>
|
||||
</div>
|
||||
<div class="col-md-4 py-2 px-1">
|
||||
<a class="btn btn-outline-primary btn-lg btn-block px-0" href="/admin/staff-audit"><i class="fal fa-book"></i> Audit log</a>
|
||||
</div>
|
||||
<div class="col-md-4 py-2 px-1">
|
||||
<a class="btn btn-outline-primary btn-lg btn-block px-0" href="/admin/error-log"><i class="fal fa-exclamation-triangle"></i> Error log</a>
|
||||
</div>
|
||||
<div class="col-md-4 py-2 px-1">
|
||||
<a class="btn btn-outline-primary btn-lg btn-block px-0" href="/admin/manage-gameservers"><i class="fal fa-server"></i> Gameservers</a>
|
||||
</div>
|
||||
<?php } if(Users::IsAdmin([Users::STAFF_CATALOG, Users::STAFF_ADMINISTRATOR])) { ?>
|
||||
<div class="col-md-4 py-2 px-1">
|
||||
<a class="btn btn-outline-success btn-lg btn-block px-0" href="/admin/create-asset"><i class="fal fa-file-plus"></i> Create asset</a>
|
||||
</div>
|
||||
<div class="col-md-4 py-2 px-1">
|
||||
<a class="btn btn-outline-success btn-lg btn-block px-0" href="/admin/give-asset"><i class="fal fa-gift"></i> Give asset</a>
|
||||
</div>
|
||||
<?php } if(Users::IsAdmin(Users::STAFF_ADMINISTRATOR)) { ?>
|
||||
<div class="col-md-4 py-2 px-1">
|
||||
<a class="btn btn-outline-success btn-lg btn-block px-0" href="/admin/give-currency"><i class="fal fa-pizza-slice"></i> Give <?=SITE_CONFIG["site"]["currency"]?></a>
|
||||
</div>
|
||||
<div class="col-md-4 py-2 px-1">
|
||||
<a class="btn btn-outline-success btn-lg btn-block px-0" href="#" onclick="polygon.buildModal({header: 'Credentials', body: '<span>You\'ll have to enter these yourself!</span> <br> Username: <code>ProjectPolygon</code> <br> Password: <code>962e8f89341b4e5f208076b5d06fb1b6</code>', buttons: [{class:'btn btn-primary px-4', attributes: {'onclick':'window.location = \'https://stats.pizzaboxer.xyz\''}, text:'Continue'}]})"><i class="fal fa-chart-pie"></i> Statistics</a>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<div class="col-md-4 py-2 px-1">
|
||||
<a class="btn btn-outline-success btn-lg btn-block px-0" href="/admin/invites"><i class="fal fa-ticket-alt"></i> Invite Tickets</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<h3 class="pb-3 font-weight-normal">Website / Server Info</h3>
|
||||
<div class="card w-100 mt-2">
|
||||
<div class="card-body text-center">
|
||||
<h3 class="font-weight-normal"><i class="fal fa-server"></i> <?=gethostname()?></h3>
|
||||
<small><?=php_uname()?></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card w-100 mt-2">
|
||||
<div class="card-body text-center">
|
||||
<h3 class="font-weight-normal"><i class="fal fa-memory"></i> <?=$usage->Memory->SytemUsage?> / <?=$usage->Memory->Total?> In Use</h3>
|
||||
<small><?=$usage->Memory->PHPUsage?> is being used by PHP</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card w-100 mt-2">
|
||||
<div class="card-body text-center">
|
||||
<h3 class="font-weight-normal"><i class="fal fa-hdd"></i> <?=$usage->Disk->SystemUsage?> / <?=$usage->Disk->Total?> Used</h3>
|
||||
<small><?=SITE_CONFIG["site"]["name"]?> is using <?=$usage->Disk->PolygonUsage?></small><br>
|
||||
<small>Thumbnail CDN is using <?=$usage->Disk->ThumbnailUsage?></small><br>
|
||||
<small>Client setup (2009-2012) is using <?=$usage->Disk->SetupUsage?> total</small>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="card w-100 mt-2">
|
||||
<div class="card-body text-center">
|
||||
<?php if(SITE_CONFIG["site"]["thumbserver"]) { ?>
|
||||
<h3 class="font-weight-normal"><i class="fal fa-images"></i> <?=$pendingRenders?> asset renders pending</h3>
|
||||
<?php if($thumbPing+35 > time()) { ?>
|
||||
<small>The thumbnail server is currently online</small>
|
||||
<?php } else { ?>
|
||||
<small>The thumbnail server last registered online at <?=date("j/n/Y g:i:s A", $thumbPing)?></small>
|
||||
<?php } } else { ?>
|
||||
<h3 class="font-weight-normal"><i class="fal fa-images"></i> Thumbserver is disabled</h3>
|
||||
<small>The thumbnail server has been manually disabled. <br> Go to /api/private/config.php to re-enable it.</small>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card w-100 mt-2">
|
||||
<div class="card-body text-center">
|
||||
<h3 class="font-weight-normal"><i class="fal fa-user"></i> <?=$usersOnline?> user<?=$usersOnline>1?'s':''?> currently online</h3>
|
||||
<?php if($usersOnline == 1) { ?><small>dead much?</small><?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php PageBuilder::BuildFooter(); ?>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$assetid = $_POST['assetID'] ?? false;
|
||||
$userid = SESSION["user"]["id"];
|
||||
|
||||
$query = $pdo->prepare("DELETE FROM ownedAssets WHERE assetId = :aid AND userId = :uid");
|
||||
$query->bindParam(":aid", $assetid, PDO::PARAM_INT);
|
||||
$query->bindParam(":uid", $userid, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
if(!$query->rowCount()) api::respond(400, false, "You do not own this asset");
|
||||
|
||||
api::respond(200, true, "OK");
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Catalog");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$Wearing = ($_POST["Wearing"] ?? "false") == "true";
|
||||
$Type = $_POST["Type"] ?? false;
|
||||
$Items = [];
|
||||
|
||||
if($Wearing)
|
||||
{
|
||||
$AssetCount = db::run(
|
||||
"SELECT COUNT(*) FROM ownedAssets WHERE userId = :UserID AND wearing = 1",
|
||||
[":UserID" => SESSION["user"]["id"]]
|
||||
)->fetchColumn();
|
||||
}
|
||||
else
|
||||
{
|
||||
$TypeString = Catalog::GetTypeByNum($Type);
|
||||
if(!Catalog::GetTypeByNum($Type)) api::respond(400, false, "Invalid asset type");
|
||||
|
||||
$AssetCount = db::run(
|
||||
"SELECT COUNT(*) FROM ownedAssets INNER JOIN assets ON assets.id = assetId WHERE userId = :UserID AND assets.type = :AssetType AND wearing = 0",
|
||||
[":UserID" => SESSION["user"]["id"], ":AssetType" => $Type]
|
||||
)->fetchColumn();
|
||||
}
|
||||
|
||||
$Pagination = Pagination($_POST["Page"] ?? 1, $AssetCount, 8);
|
||||
|
||||
if($Pagination->Pages == 0)
|
||||
{
|
||||
api::respond(200, true, $Wearing ? "You are not currently wearing anything" : "You don't have any unequipped ".plural($TypeString)." to wear");
|
||||
}
|
||||
|
||||
if($Wearing)
|
||||
{
|
||||
$Assets = db::run(
|
||||
"SELECT assets.* FROM ownedAssets
|
||||
INNER JOIN assets ON assets.id = assetId
|
||||
WHERE userId = :UserID AND wearing = 1
|
||||
ORDER BY last_toggle DESC LIMIT 8 OFFSET :Offset",
|
||||
[":UserID" => SESSION["user"]["id"], ":Offset" => $Pagination->Offset]
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$Assets = db::run(
|
||||
"SELECT assets.* FROM ownedAssets
|
||||
INNER JOIN assets ON assets.id = assetId
|
||||
WHERE userId = :UserID AND assets.type = :AssetType AND wearing = 0
|
||||
ORDER BY timestamp DESC LIMIT 8 OFFSET :Offset",
|
||||
[":UserID" => SESSION["user"]["id"], ":AssetType" => $Type, ":Offset" => $Pagination->Offset]
|
||||
);
|
||||
}
|
||||
|
||||
while($asset = $Assets->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$Items[] =
|
||||
[
|
||||
"url" => "/".encode_asset_name($asset->name)."-item?id=".$asset->id,
|
||||
"item_id" => $asset->id,
|
||||
"item_name" => htmlspecialchars($asset->name),
|
||||
"item_thumbnail" => Thumbnails::GetAsset($asset)
|
||||
];
|
||||
}
|
||||
|
||||
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $Pagination->Pages, "items" => $Items]));
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
|
||||
Polygon::ImportClass("RBXClient");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$userid = SESSION["user"]["id"];
|
||||
$bodyColors = json_decode(SESSION["user"]["bodycolors"]);
|
||||
$bodyPart = $_POST["BodyPart"] ?? false;
|
||||
$color = $_POST["Color"] ?? false;
|
||||
|
||||
if(!$color || !in_array($bodyPart, ["Head", "Torso", "Left Arm", "Right Arm", "Left Leg", "Right Leg"])) api::respond(400, false, "Bad Request");
|
||||
|
||||
$brickcolor = RBXClient::HexToBrickColor(rgbtohex($color));
|
||||
if(!$brickcolor) api::respond(200, false, "Invalid body color #".rgbtohex($color));
|
||||
|
||||
$bodyColors->{$bodyPart} = $brickcolor;
|
||||
$bodyColors = json_encode($bodyColors);
|
||||
|
||||
$query = $pdo->prepare("UPDATE users SET bodycolors = :bodycolors WHERE id = :uid");
|
||||
$query->bindParam(":bodycolors", $bodyColors, PDO::PARAM_STR);
|
||||
$query->bindParam(":uid", $userid, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
|
||||
api::respond(200, true, "OK");
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
// here we do the render request synchronously so that the avatar can be refreshed asap
|
||||
Polygon::RequestRender("Avatar", SESSION["user"]["id"], false);
|
||||
|
||||
// api::respond(200, true, "OK");
|
||||
api::respond(200, true, Thumbnails::GetAvatar(SESSION["user"]["id"], 420, 420));
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$assetid = $_POST['assetID'] ?? false;
|
||||
$userid = SESSION["user"]["id"];
|
||||
|
||||
$query = $pdo->prepare("SELECT wearing, assets.* FROM ownedAssets INNER JOIN assets ON assets.id = assetId WHERE userId = :uid AND assetId = :aid");
|
||||
$query->bindParam(":aid", $assetid, PDO::PARAM_INT);
|
||||
$query->bindParam(":uid", $userid, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
$info = $query->fetch(PDO::FETCH_OBJ);
|
||||
if(!$info) api::respond(400, false, "You do not own this asset");
|
||||
|
||||
$wear = !$info->wearing;
|
||||
|
||||
if(in_array($info->type, [2, 11, 12, 17, 18])) //asset types that can only have one worn at a time
|
||||
{
|
||||
$query = $pdo->prepare("UPDATE ownedAssets INNER JOIN assets ON assets.id = assetId SET wearing = 0 WHERE userId = :uid AND type = :type");
|
||||
$query->bindParam(":uid", $userid, PDO::PARAM_INT);
|
||||
$query->bindParam(":type", $info->type, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
|
||||
if($wear)
|
||||
{
|
||||
$query = $pdo->prepare("UPDATE ownedAssets SET wearing = 1, last_toggle = UNIX_TIMESTAMP() WHERE userId = :uid AND assetId = :aid");
|
||||
$query->bindParam(":aid", $assetid, PDO::PARAM_INT);
|
||||
$query->bindParam(":uid", $userid, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
}
|
||||
}
|
||||
elseif($info->type == 8) //up to 3 hats can be worn at the same time
|
||||
{
|
||||
if($wear)
|
||||
{
|
||||
$query = $pdo->prepare("SELECT COUNT(*) FROM ownedAssets INNER JOIN assets ON assets.id = assetId WHERE userId = :uid AND type = 8 AND wearing");
|
||||
$query->bindParam(":uid", $userid, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
if($query->fetchColumn() >= 5) api::respond(400, false, "You cannot wear more than 5 hats at a time");
|
||||
}
|
||||
|
||||
$query = $pdo->prepare("UPDATE ownedAssets SET wearing = :wear, last_toggle = UNIX_TIMESTAMP() WHERE userId = :uid AND assetId = :aid");
|
||||
$query->bindParam(":wear", $wear, PDO::PARAM_INT);
|
||||
$query->bindParam(":aid", $assetid, PDO::PARAM_INT);
|
||||
$query->bindParam(":uid", $userid, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
}
|
||||
elseif($info->type == 19) //no limit to how many gears can be equipped
|
||||
{
|
||||
$query = $pdo->prepare("UPDATE ownedAssets SET wearing = :wear, last_toggle = UNIX_TIMESTAMP() WHERE userId = :uid AND assetId = :aid");
|
||||
$query->bindParam(":wear", $wear, PDO::PARAM_INT);
|
||||
$query->bindParam(":aid", $assetid, PDO::PARAM_INT);
|
||||
$query->bindParam(":uid", $userid, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
}
|
||||
else
|
||||
{
|
||||
api::respond(400, false, "You cannot wear this asset!");
|
||||
}
|
||||
|
||||
api::respond(200, true, "OK");
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
api::initialize(["method" => "POST", "secure" => true, "logged_in" => true]);
|
||||
|
||||
$userid = SESSION["user"]["id"];
|
||||
$sessionkey = SESSION['sessionKey'];
|
||||
|
||||
$sesscount = $pdo->prepare("SELECT COUNT(*) FROM sessions WHERE userId = :uid AND valid AND NOT sessionKey = :key");
|
||||
$sesscount->bindParam(":uid", $userid, PDO::PARAM_INT);
|
||||
$sesscount->bindParam(":key", $sessionkey, PDO::PARAM_STR);
|
||||
$sesscount->execute();
|
||||
|
||||
if(!$sesscount->fetchColumn()) api::respond(400, false, "There are no other sessions to log out of");
|
||||
|
||||
$query = $pdo->prepare("UPDATE sessions SET valid = 0 WHERE userId = :uid AND valid AND NOT sessionKey = :key");
|
||||
$query->bindParam(":uid", $userid, PDO::PARAM_INT);
|
||||
$query->bindParam(":key", $sessionkey, PDO::PARAM_STR);
|
||||
$query->execute();
|
||||
|
||||
api::respond(200, true, "OK");
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Groups");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true]);
|
||||
|
||||
$FeedResults = db::run(
|
||||
"SELECT feed.*, users.username FROM feed
|
||||
INNER JOIN users ON users.id = feed.userId WHERE userId = :uid
|
||||
OR groupId IS NULL AND userId IN
|
||||
(
|
||||
SELECT (CASE WHEN requesterId = :uid THEN receiverId ELSE requesterId END) FROM friends
|
||||
WHERE :uid IN (requesterId, receiverId) AND status = 1
|
||||
)
|
||||
OR groupId IN
|
||||
(
|
||||
SELECT groups_members.GroupID FROM groups_members
|
||||
INNER JOIN groups_ranks ON groups_ranks.GroupID = groups_members.GroupID AND groups_ranks.Rank = groups_members.Rank
|
||||
WHERE groups_members.UserID = :uid AND groups_ranks.permissions LIKE '%\"CanViewGroupStatus\":true%'
|
||||
)
|
||||
ORDER BY feed.id DESC LIMIT 15",
|
||||
[":uid" => SESSION["user"]["id"]]
|
||||
);
|
||||
|
||||
$feed = [];
|
||||
$news = [];
|
||||
|
||||
/*$news[] =
|
||||
[
|
||||
"header" => '<h4 class="font-weight-normal">lol</h4>',
|
||||
"message" => 'fucked your mom'
|
||||
];*/
|
||||
|
||||
/*$news[] =
|
||||
[
|
||||
"header" => '<h4 class="font-weight-normal">this isn\'t dead!!!! (probably)</h4>',
|
||||
"message" => "ive been more inclined to work on polygon now after like 4 months, so i guess development has resumed <br><br> 2fa has been implemented, and next on the roadmap is the catalog system. so yeah, stay tuned for that"
|
||||
];*/
|
||||
|
||||
/* $news[] =
|
||||
[
|
||||
"header" => "",
|
||||
"img" => "https://media.discordapp.net/attachments/745025397749448814/835635922590629888/HDKolobok-256px-3.gif",
|
||||
// "message" => "What you know about KOLONBOK. ™ "
|
||||
"message" => "KOLONBOK. ™ Has fix 2009"
|
||||
]; */
|
||||
|
||||
/* $news[] =
|
||||
[
|
||||
"header" => "Groups have been released!",
|
||||
"img" => "/img/ProjectPolygon.png",
|
||||
"message" => "Groups have now been fully released, with more functionality than you could ever imagine. Groups don't cost anything to make, and you can join up to 20 of them. <br> If you haven't yet, come join the <a href=\"/groups?gid=1\">official group</a>!"
|
||||
]; */
|
||||
|
||||
/* $news[] =
|
||||
[
|
||||
"header" => "",
|
||||
"img" => "https://media.discordapp.net/attachments/745025397749448814/835635922590629888/HDKolobok-256px-3.gif",
|
||||
"message" => "What you know about KOLONBOK. ™ "
|
||||
]; */
|
||||
|
||||
while($row = $FeedResults->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$timestamp = timeSince($row->timestamp);
|
||||
|
||||
if($row->groupId == NULL)
|
||||
{
|
||||
$feed[] =
|
||||
[
|
||||
"userName" => $row->username,
|
||||
"img" => Thumbnails::GetAvatar($row->userId),
|
||||
"header" => "<p class=\"m-0\"><a href=\"/user?ID={$row->userId}\">{$row->username}</a> - <small>{$timestamp}</small></p>",
|
||||
"message" => Polygon::FilterText($row->text)
|
||||
];
|
||||
}
|
||||
else
|
||||
{
|
||||
$GroupInfo = Groups::GetGroupInfo($row->groupId, true, true);
|
||||
$GroupInfo->name = htmlspecialchars($GroupInfo->name);
|
||||
|
||||
$feed[] =
|
||||
[
|
||||
"userName" => $GroupInfo->name,
|
||||
"img" => Thumbnails::GetAssetFromID($GroupInfo->emblem),
|
||||
"header" => "<p class=\"m-0\"><a href=\"/groups?gid={$GroupInfo->id}\">{$GroupInfo->name}</a> - <small>posted by <a href=\"/user?ID={$row->userId}\">{$row->username}</a></small> - <small>{$timestamp}</small></p>",
|
||||
"message" => Polygon::FilterText($row->text)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$FeedCount = $FeedResults->rowCount();
|
||||
|
||||
if($FeedCount < 15)
|
||||
{
|
||||
$feed[] =
|
||||
[
|
||||
"userName" => "Your feed is currently empty!",
|
||||
"img" => "/img/feed/friends.png",
|
||||
"header" => "<h4 class=\"font-weight-normal\">Looks like your feed's empty</h4>",
|
||||
"message" => "If you haven't made any friends yet, <a href='/browse'>go make some</a>! <br> If you already have some, why don't you kick off the discussion?"
|
||||
];
|
||||
|
||||
if($FeedCount < 14)
|
||||
{
|
||||
$feed[] =
|
||||
[
|
||||
"userName" => "Customize your character",
|
||||
"img" => "/img/feed/cart.png",
|
||||
"header" => "<h4 class=\"font-weight-normal\">Customize your character</h4>",
|
||||
"message" => "Log in every day and earn 10 pizzas. Pizzas can be used to buy clothing in our <a href=\"/catalog\">catalog</a>. You can also create your own clothing on the <a href=\"/develop\">Build page</a>."
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
api::respond_custom(["status" => 200, "success" => true, "message" => "OK", "feed" => $feed, "news" => $news]);
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Games");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true]);
|
||||
|
||||
$userid = SESSION["user"]["id"];
|
||||
$items = [];
|
||||
|
||||
$Places = db::run(
|
||||
"SELECT assets.* FROM GameJobSessions
|
||||
INNER JOIN GameJobs ON GameJobSessions.JobID = GameJobs.JobID
|
||||
INNER JOIN assets ON assets.id = GameJobs.PlaceID
|
||||
WHERE UserID = :UserID AND Verified
|
||||
ORDER BY GameJobSessions.TimeCreated DESC LIMIT 12",
|
||||
[":UserID" => SESSION["user"]["id"]]
|
||||
);
|
||||
|
||||
while($Place = $Places->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$items[] =
|
||||
[
|
||||
"PlaceID" => $Place->id,
|
||||
"Name" => Polygon::FilterText($Place->name),
|
||||
"Location" => "/" . encode_asset_name($Place->name) . "-place?id={$Place->id}",
|
||||
"Thumbnail" => Thumbnails::GetAsset($Place, 768, 432),
|
||||
"OnlinePlayers" => $Place->ActivePlayers
|
||||
];
|
||||
}
|
||||
|
||||
api::respond_custom(["status" => 200, "success" => true, "message" => "OK", "items" => $items]);
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$userid = SESSION["user"]["id"];
|
||||
$type = $_POST["type"] ?? false;
|
||||
$Items = [];
|
||||
|
||||
if (!in_array($type, ["Purchases", "Sales"])) api::respond(400, false, "Bad Request");
|
||||
|
||||
if ($type == "Sales")
|
||||
{
|
||||
$SelfIdentifier = "seller";
|
||||
$MemberIdentifier = "purchaser";
|
||||
$Action = "sold";
|
||||
}
|
||||
else
|
||||
{
|
||||
$SelfIdentifier = "purchaser";
|
||||
$MemberIdentifier = "seller";
|
||||
$Action = "purchased";
|
||||
}
|
||||
|
||||
$TransactionCount = db::run(
|
||||
"SELECT COUNT(*) FROM transactions WHERE {$SelfIdentifier} = :UserID",
|
||||
[":UserID" => SESSION["user"]["id"]]
|
||||
)->fetchColumn();
|
||||
|
||||
$Pagination = Pagination($_POST["page"] ?? 1, $TransactionCount, 15);
|
||||
|
||||
if($Pagination->Pages == 0) api::respond(200, true, "You have not {$Action} any items!");
|
||||
|
||||
$Transactions = db::run(
|
||||
"SELECT transactions.*, users.username, assets.name FROM transactions
|
||||
INNER JOIN users ON users.id = {$MemberIdentifier} INNER JOIN assets ON assets.id = transactions.assetId
|
||||
WHERE {$SelfIdentifier} = :UserID ORDER BY id DESC LIMIT 15 OFFSET :Offset",
|
||||
[":UserID" => SESSION["user"]["id"], ":Offset" => $Pagination->Offset]
|
||||
);
|
||||
|
||||
while($Transaction = $Transactions->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$MemberID = $type == "Sales" ? $Transaction->purchaser : $Transaction->seller;
|
||||
|
||||
$Items[] =
|
||||
[
|
||||
"type" => $type == "Sales" ? "Sold" : "Purchased",
|
||||
"date" => date('j/n/y', $Transaction->timestamp),
|
||||
"member_name" => $Transaction->username,
|
||||
"member_id" => $MemberID,
|
||||
"member_avatar" => Thumbnails::GetAvatar($MemberID),
|
||||
"asset_name" => Polygon::FilterText($Transaction->name),
|
||||
"asset_id" => $Transaction->assetId,
|
||||
"amount" => $Transaction->amount
|
||||
];
|
||||
}
|
||||
|
||||
api::respond_custom(["status" => 200, "success" => true, "message" => "OK", "items" => $Items, "pages" => $Pagination->Pages]);
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Auth");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
if(!isset($_POST['currentpwd']) || !isset($_POST['newpwd']) || !isset($_POST['confnewpwd'])) api::respond(400, false, "Bad Request");
|
||||
|
||||
$userid = SESSION["user"]["id"];
|
||||
$row = (object)SESSION["user"];
|
||||
$currentpwd = new Auth($_POST['currentpwd']);
|
||||
$newpwd = new Auth($_POST['newpwd']);
|
||||
|
||||
if($row->lastpwdchange+1800 > time()) api::respond(429, false, "Please wait ".ceil((($row->lastpwdchange+1800)-time())/60)." minutes before attempting to change your password again");
|
||||
if(!$currentpwd->VerifyPassword($row->password)) api::respond(400, false, "Your current password does not match");
|
||||
if($_POST['currentpwd'] == $_POST['newpwd']) api::respond(400, false, "Your new password cannot be the same as your current one");
|
||||
if(strlen(preg_replace('/[0-9]/', "", $_POST['newpwd'])) < 6) api::respond(400, false, "Your new password is too weak. Make sure it contains at least six non-numeric characters");
|
||||
if(strlen(preg_replace('/[^0-9]/', "", $_POST['newpwd'])) < 2) api::respond(400, false, "Your new password is too weak. Make sure it contains at least two numbers");
|
||||
if($_POST['newpwd'] != $_POST['confnewpwd']) api::respond(400, false, "Confirmation password does not match");
|
||||
|
||||
$newpwd->UpdatePassword($userid);
|
||||
api::respond(200, true, "OK");
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
Users::UpdatePing();
|
||||
api::respond_custom(["status" => 200, "success" => true, "message" => "OK", "friendRequests" => (int)SESSION["friendRequests"]]);
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
api::initialize(["method" => "POST", "secure" => true, "logged_in" => true]);
|
||||
|
||||
if(!isset($_POST['blurb']) || !isset($_POST['theme']) || !isset($_POST['filter'])) api::respond(400, false, "Bad Request");
|
||||
|
||||
$userid = SESSION["user"]["id"];
|
||||
$filter = (int)($_POST['filter'] == 'true');
|
||||
$debugging = (int)(isset($_POST['debugging']) && $_POST['debugging'] == 'true');
|
||||
|
||||
if(!in_array($_POST['theme'], ["light", "dark", "hitius", "2014"])) api::respond(200, false, "Invalid theme");
|
||||
|
||||
if(!strlen($_POST['blurb'])) api::respond(200, false, "Your blurb can't be empty");
|
||||
if(strlen($_POST['blurb']) > 1000) api::respond(200, false, "Your blurb is too large");
|
||||
if(Polygon::IsExplicitlyFiltered($_POST["blurb"])) api::respond(200, false, "Your blurb contains inappropriate text");
|
||||
|
||||
db::run(
|
||||
"UPDATE users SET blurb = :blurb, filter = :filter, theme = :theme, debugging = :debugging WHERE id = :uid",
|
||||
[":uid" => $userid, ":blurb" => $_POST['blurb'], ":filter" => $filter, ":theme" => $_POST['theme'], ":debugging" => $debugging]
|
||||
);
|
||||
|
||||
api::respond(200, true, "Your settings have been updated");
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Catalog");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
Polygon::ImportClass("Discord");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$userId = SESSION["user"]["id"];
|
||||
$status = $_POST['status'] ?? false;
|
||||
|
||||
if(!strlen($status)) api::respond(200, false, "Your status cannot be empty");
|
||||
if(strlen($status) > 140) api::respond(200, false, "Your status cannot be more than 140 characters");
|
||||
|
||||
//ratelimit
|
||||
$query = db::run(
|
||||
"SELECT timestamp FROM feed WHERE userId = :uid AND groupID IS NULL AND timestamp+300 > UNIX_TIMESTAMP()",
|
||||
[":uid" => $userId]
|
||||
);
|
||||
|
||||
if($query->rowCount())
|
||||
api::respond(200, false, "Please wait ".GetReadableTime($query->fetchColumn(), ["RelativeTime" => "5 minutes"])." before updating your status");
|
||||
|
||||
db::run("INSERT INTO feed (userId, timestamp, text) VALUES (:uid, UNIX_TIMESTAMP(), :status)", [":uid" => $userId, ":status" => $status]);
|
||||
|
||||
db::run("UPDATE users SET status = :status WHERE id = :uid", [":uid" => $userId, ":status" => $status]);
|
||||
|
||||
if(time() < strtotime("2021-09-07 00:00:00") && stripos($status, "#bezosgang") !== false && !Catalog::OwnsAsset(SESSION["user"]["id"], 2802))
|
||||
{
|
||||
db::run(
|
||||
"INSERT INTO ownedAssets (assetId, userId, timestamp) VALUES (2802, :uid, UNIX_TIMESTAMP())",
|
||||
[":uid" => SESSION["user"]["id"]]
|
||||
);
|
||||
}
|
||||
|
||||
Discord::SendToWebhook(
|
||||
[
|
||||
"username" => SESSION["user"]["username"],
|
||||
"content" => $status,
|
||||
"avatar_url" => Thumbnails::GetAvatar(SESSION["user"]["id"])
|
||||
],
|
||||
Discord::WEBHOOK_KUSH
|
||||
);
|
||||
|
||||
api::respond(200, true, "OK");
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Gzip");
|
||||
|
||||
if (!isset($_GET["mode"]) || !in_array($_GET["mode"], ["versions", "data"])) die(http_response_code(400));
|
||||
if (!isset($_GET["id"]) || !is_numeric($_GET["id"])) die(http_response_code(400));
|
||||
|
||||
if ($_GET["mode"] == "data")
|
||||
{
|
||||
header("content-type: text/plain");
|
||||
|
||||
if (isset($_GET["version"]) && !is_numeric($_GET["version"])) die(http_response_code(400));
|
||||
|
||||
/* $curl = curl_init();
|
||||
|
||||
curl_setopt_array($curl,
|
||||
[
|
||||
CURLOPT_URL => "https://assetdelivery.roblox.com/v1/asset/?id=".$_GET["id"].(isset($_GET["version"]) ? "&version=".$_GET["version"] : ""),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_HTTPHEADER => ["User-Agent: Roblox/WinInet"]
|
||||
]);
|
||||
|
||||
$Data = curl_exec($curl);
|
||||
$StatusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
|
||||
http_response_code($StatusCode);
|
||||
die($Data); */
|
||||
|
||||
$context = stream_context_create(
|
||||
[
|
||||
'http' =>
|
||||
[
|
||||
'method' => 'GET',
|
||||
'header' => 'User-Agent: Roblox/WinInet',
|
||||
'ignore_errors' => true
|
||||
]
|
||||
]);
|
||||
|
||||
$response = file_get_contents("https://assetdelivery.roblox.com/v1/asset/?id=".$_GET["id"].(isset($_GET["version"]) ? "&version=".$_GET["version"] : ""), false, $context);
|
||||
|
||||
if ($http_response_header[0] == "HTTP/1.1 302 Found")
|
||||
{
|
||||
$http_response_header[0] = "HTTP/1.0 200 OK";
|
||||
}
|
||||
|
||||
header($http_response_header[0]);
|
||||
|
||||
if (Gzip::IsGzEncoded($response))
|
||||
die(gzdecode($response));
|
||||
else
|
||||
die($response);
|
||||
}
|
||||
else
|
||||
{
|
||||
header("content-type: application/json");
|
||||
|
||||
$Versions = [];
|
||||
$Version = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
$Version++;
|
||||
$curl = curl_init();
|
||||
|
||||
curl_setopt_array($curl,
|
||||
[
|
||||
CURLOPT_URL => "https://assetdelivery.roblox.com/v1/asset/?id=".$_GET["id"]."&version=".$Version,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_HEADER => true,
|
||||
CURLOPT_HTTPHEADER => ["User-Agent: Roblox/WinInet"]
|
||||
]);
|
||||
|
||||
$Data = curl_exec($curl);
|
||||
$StatusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
|
||||
if ($StatusCode == 500) continue;
|
||||
if ($StatusCode != 200) break;
|
||||
|
||||
$HeaderSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
|
||||
$HeadersText = substr($Data, 0, $HeaderSize);
|
||||
$CDNHeadersText = explode("\r\n\r\n", $HeadersText)[1];
|
||||
$CDNHeadersArray = [];
|
||||
|
||||
foreach (explode("\r\n", $CDNHeadersText) as $i => $line)
|
||||
{
|
||||
if ($i === 0)
|
||||
{
|
||||
$CDNHeadersArray['http_code'] = $line;
|
||||
}
|
||||
else
|
||||
{
|
||||
list ($key, $value) = explode(': ', $line);
|
||||
$CDNHeadersArray[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$Versions[$Version] = $CDNHeadersArray["last-modified"];
|
||||
}
|
||||
|
||||
die(json_encode($Versions));
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Forum");
|
||||
|
||||
api::initialize(["method" => "POST", "admin" => [Users::STAFF_MODERATOR, Users::STAFF_ADMINISTRATOR], "admin_ratelimit" => true, "secure" => true]);
|
||||
|
||||
if(!isset($_POST['postType'])){ api::respond(400, false, "Bad Request"); }
|
||||
if(!in_array($_POST['postType'], ["thread", "reply"])){ api::respond(400, false, "Bad Request"); }
|
||||
if(!isset($_POST['postId'])){ api::respond(400, false, "Bad Request"); }
|
||||
if(!is_numeric($_POST['postId'])){ api::respond(400, false, "Bad Request"); }
|
||||
|
||||
$userid = SESSION["user"]["id"];
|
||||
$isThread = $_POST['postType'] == "thread";
|
||||
$threadInfo = $isThread ? Forum::GetThreadInfo($_POST['postId']) : Forum::GetReplyInfo($_POST['postId']);
|
||||
|
||||
if(!$threadInfo){ api::respond(400, false, "Post does not exist"); }
|
||||
|
||||
$query = $isThread ? $pdo->prepare("UPDATE forum_threads SET deleted = 1 WHERE id = :id") : $pdo->prepare("UPDATE forum_replies SET deleted = 1 WHERE id = :id");
|
||||
$query->bindParam(":id", $_POST['postId'], PDO::PARAM_INT);
|
||||
|
||||
if($query->execute()){ Users::LogStaffAction("[ Forums ] Deleted forum ".($isThread?"thread":"reply")." ID ".$_POST['postId']); api::respond(200, true, "OK"); }
|
||||
else{ api::respond(500, false, "Internal Server Error"); }
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Auth");
|
||||
|
||||
$password = $_GET["password"] ?? "";
|
||||
$auth = new Auth($password);
|
||||
echo $auth->CreatePassword();
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Catalog");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "admin" => [Users::STAFF_CATALOG, Users::STAFF_ADMINISTRATOR], "logged_in" => true, "secure" => true]);
|
||||
|
||||
$type = $_POST["type"] ?? false;
|
||||
$page = $_POST["page"] ?? 1;
|
||||
$assets = [];
|
||||
|
||||
if(!Catalog::GetTypeByNum($type)) api::respond(400, false, "Invalid asset type");
|
||||
|
||||
$query = $pdo->prepare("SELECT COUNT(*) FROM assets WHERE creator = 2 AND type = :type ORDER BY id DESC");
|
||||
$query->bindParam(":type", $type, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
|
||||
$pages = ceil($query->fetchColumn()/15);
|
||||
$offset = ($page - 1)*15;
|
||||
|
||||
$query = $pdo->prepare("SELECT * FROM assets WHERE creator = 2 AND type = :type ORDER BY id DESC LIMIT 15 OFFSET $offset");
|
||||
$query->bindParam(":type", $type, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
|
||||
while($asset = $query->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$info = Catalog::GetAssetInfo($asset->id);
|
||||
|
||||
$assets[] =
|
||||
[
|
||||
"name" => htmlspecialchars($asset->name),
|
||||
"id" => $asset->id,
|
||||
"thumbnail" => Thumbnails::GetAsset($asset),
|
||||
"item_url" => "/".encode_asset_name($asset->name)."-item?id=".$asset->id,
|
||||
"config_url" => "/my/item?ID=".$asset->id,
|
||||
"created" => date("n/j/Y", $asset->created),
|
||||
"sales-total" => $info->sales_total,
|
||||
"sales-week" => $info->sales_week
|
||||
];
|
||||
}
|
||||
|
||||
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "assets" => $assets, "pages" => $pages]));
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "admin" => Users::STAFF, "secure" => true]);
|
||||
|
||||
$id = $_POST["id"];
|
||||
$category = $_POST["category"];
|
||||
$type = $_POST["type"] ?? false;
|
||||
$page = $_POST["page"] ?? 1;
|
||||
$result = [];
|
||||
|
||||
if(!in_array($category, ["User", "Asset"])) api::respond(400, false, "Bad Request");
|
||||
if(!in_array($type, ["Purchases", "Sales"])) api::respond(400, false, "Bad Request");
|
||||
|
||||
if ($category == "User")
|
||||
{
|
||||
$selector = $type == "Sales" ? "seller" : "purchaser";
|
||||
$member = $type == "Sales" ? "purchaser" : "seller";
|
||||
|
||||
}
|
||||
else if ($category == "Asset")
|
||||
{
|
||||
$selector = "assetId";
|
||||
$member = "purchaser";
|
||||
}
|
||||
|
||||
$count = db::run("SELECT COUNT(*) FROM transactions WHERE $selector = :id", [":id" => $id])->fetchColumn();
|
||||
|
||||
$pages = ceil($count/15);
|
||||
$offset = ($page - 1)*15;
|
||||
|
||||
$transactions = db::run(
|
||||
"SELECT transactions.*, users.username, assets.name FROM transactions
|
||||
INNER JOIN users ON $member = users.id
|
||||
INNER JOIN assets ON transactions.assetId = assets.id
|
||||
WHERE $selector = :id ORDER BY id DESC LIMIT 15 OFFSET $offset",
|
||||
[":id" => $id]
|
||||
);
|
||||
|
||||
if(!$transactions->rowCount()) api::respond(200, true, "No transactions have been logged");
|
||||
|
||||
while($transaction = $transactions->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$memberID = $member == "purchaser" ? $transaction->purchaser : $transaction->seller;
|
||||
|
||||
$result[] =
|
||||
[
|
||||
"type" => $type == "Sales" ? "Sold" : "Purchased",
|
||||
"date" => date('j/n/y', $transaction->timestamp),
|
||||
"member_name" => $transaction->username,
|
||||
"member_id" => $memberID,
|
||||
"member_avatar" => Thumbnails::GetAvatar($memberID),
|
||||
"asset_name" => htmlspecialchars($transaction->name),
|
||||
"asset_id" => $transaction->assetId,
|
||||
"amount" => $transaction->amount,
|
||||
"flagged" => (bool) $transaction->flagged
|
||||
];
|
||||
}
|
||||
|
||||
api::respond_custom(["status" => 200, "success" => true, "message" => "OK", "transactions" => $result, "pages" => $pages]);
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Catalog");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "admin" => Users::STAFF, "secure" => true]);
|
||||
|
||||
$page = $_POST["page"] ?? 1;
|
||||
$assets = [];
|
||||
|
||||
$query = $pdo->query("SELECT COUNT(*) FROM assets WHERE NOT approved AND type != 1");
|
||||
$pages = ceil($query->fetchColumn()/18);
|
||||
$offset = ($page - 1)*18;
|
||||
|
||||
if(!$pages) api::respond(200, true, "There are no assets to approve");
|
||||
|
||||
$query = $pdo->prepare(
|
||||
"SELECT assets.*, users.username FROM assets
|
||||
INNER JOIN users ON creator = users.id
|
||||
WHERE NOT approved AND type != 1
|
||||
LIMIT 18 OFFSET :offset"
|
||||
);
|
||||
$query->bindParam(":offset", $offset, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
|
||||
while($asset = $query->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$assets[] =
|
||||
[
|
||||
"url" => "item?ID=".$asset->id,
|
||||
"item_id" => $asset->id,
|
||||
"item_name" => htmlspecialchars($asset->name),
|
||||
"item_thumbnail" => Thumbnails::GetAsset($asset, 420, 420, true),
|
||||
"texture_id" => $asset->type == 22 ? $asset->id : $asset->imageID,
|
||||
"creator_id" => $asset->creator,
|
||||
"creator_name" => $asset->username,
|
||||
"type" => Catalog::GetTypeByNum($asset->type),
|
||||
"created" => date("j/n/y G:i A", $asset->created),
|
||||
"price" => $asset->sale ? $asset->price ? '<i class="fal fa-pizza-slice"></i> '.$asset->price : "Free" : "Off-Sale"
|
||||
];
|
||||
}
|
||||
|
||||
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $pages, "assets" => $assets]));
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
header("content-type: text/plain");
|
||||
|
||||
// this should only be used if core.php does not work
|
||||
$emergency = ($_GET["key"] ?? false) == "D5F6E2EAA6C07C991CA2895920A8BBA8BB66CA16";
|
||||
$output = "";
|
||||
$webhook = "";
|
||||
$output_array = [];
|
||||
|
||||
if($emergency)
|
||||
{
|
||||
require $_SERVER["DOCUMENT_ROOT"]."/api/private/components/Discord.php";
|
||||
|
||||
$webhook .= sprintf("[%s] Git Pull intiated by %s on %s\n", date('d/m/Y h:i:s A'), "[[[OVERRIDE]]]", $_SERVER["HTTP_HOST"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
require $_SERVER["DOCUMENT_ROOT"]."/api/private/core.php";
|
||||
Polygon::ImportClass("Discord");
|
||||
|
||||
if(!Users::IsAdmin(Users::STAFF_ADMINISTRATOR)) die(http_response_code(404));
|
||||
$webhook .= sprintf("[%s] Git Pull executed by %s on %s\n", date('d/m/Y h:i:s A'), SESSION["user"]["username"], $_SERVER["HTTP_HOST"]);
|
||||
}
|
||||
|
||||
exec("git pull 2>&1", $output_array, $exitcode);
|
||||
|
||||
foreach($output_array as $line) $output .= "$line\n";
|
||||
if($exitcode != 0) $output .= "\n\nGit exited with code $exitcode";
|
||||
|
||||
echo $output;
|
||||
|
||||
$webhook .= "```yaml\n";
|
||||
$webhook .= $output;
|
||||
$webhook .= "```";
|
||||
|
||||
Discord::SendToWebhook(["content" => $webhook], Discord::WEBHOOK_POLYGON_GITPULL, false);
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
api::initialize(["method" => "POST", "admin" => Users::STAFF_ADMINISTRATOR, "admin_ratelimit" => true, "secure" => true]);
|
||||
|
||||
if(SESSION["user"]["id"] != 1){ api::respond(400, false, "Insufficient admin level"); }
|
||||
if(!isset($_POST["username"]) || !isset($_POST["amount"]) || !isset($_POST["reason"])){ api::respond(400, false, "Invalid Request"); }
|
||||
if(!trim($_POST["username"])){ api::respond(400, false, "You haven't set a username"); }
|
||||
|
||||
if(!$_POST["amount"]){ api::respond(400, false, "You haven't set the amount of ".SITE_CONFIG["site"]["currency"]." to give"); }
|
||||
if(!is_numeric($_POST["amount"])){ api::respond(400, false, "The amount of ".SITE_CONFIG["site"]["currency"]." to give must be numerical"); }
|
||||
if($_POST["amount"] > 500 || $_POST["amount"] < -500){ api::respond(400, false, "Maximum amount of ".SITE_CONFIG["site"]["currency"]." you can give/take is 500 at a time"); }
|
||||
|
||||
if(!trim($_POST["reason"])){ api::respond(400, false, "You must set a reason"); }
|
||||
|
||||
$amount = $_POST["amount"];
|
||||
$userInfo = Users::GetInfoFromName($_POST["username"]);
|
||||
if(!$userInfo){ api::respond(400, false, "That user doesn't exist"); }
|
||||
if(($userInfo->currency + $_POST["amount"]) < 0){ api::respond(400, false, "That'll make the user go bankrupt!"); }
|
||||
|
||||
$query = $pdo->prepare("UPDATE users SET currency = currency+:amount WHERE id = :uid");
|
||||
$query->bindParam(":amount", $amount, PDO::PARAM_INT);
|
||||
$query->bindParam(":uid", $userInfo->id, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
|
||||
Users::LogStaffAction("[ Currency ] Gave ".$_POST["amount"]." ".SITE_CONFIG["site"]["currency"]." to ".$userInfo->username." ( user ID ".$userInfo->id." ) ( Reason: ".$_POST["reason"]." )");
|
||||
api::respond(200, true, "Gave ".$_POST["amount"]." ".SITE_CONFIG["site"]["currency"]." to ".$userInfo->username);
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
|
||||
Polygon::ImportClass("Catalog");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "admin" => Users::STAFF, "admin_ratelimit" => true, "secure" => true]);
|
||||
|
||||
$assetId = $_POST['assetID'] ?? false;
|
||||
$action = $_POST['action'] ?? false;
|
||||
$action_sql = $action == "approve" ?: 2;
|
||||
$reason = $_POST['reason'] ?? false;
|
||||
$asset = Catalog::GetAssetInfo($assetId);
|
||||
|
||||
if (!in_array($action, ["approve", "decline"])) api::respond(400, false, "Invalid request");
|
||||
if (!$asset) api::respond(200, false, "Asset does not exist");
|
||||
if ($action == "approve" && $asset->approved == 1) api::respond(200, false, "This asset has already been approved");
|
||||
if ($action == "disapprove" && $asset->approved == 2) api::respond(200, false, "This asset has already been disapproved");
|
||||
if ($action == "approve" && $asset->approved == 2) api::respond(200, false, "Disapproved assets cannot be reapproved");
|
||||
|
||||
db::run(
|
||||
"UPDATE assets SET approved = :action WHERE id IN (:id, :image)",
|
||||
[":action" => $action_sql, ":id" => $asset->id, ":image" => $asset->imageID]
|
||||
);
|
||||
|
||||
if ($action == "decline")
|
||||
{
|
||||
Thumbnails::DeleteAsset($asset->id);
|
||||
Catalog::DeleteAsset($asset->id);
|
||||
|
||||
if ($asset->imageID != NULL)
|
||||
{
|
||||
Catalog::DeleteAsset($asset->imageID);
|
||||
Thumbnails::DeleteAsset($asset->imageID);
|
||||
}
|
||||
}
|
||||
|
||||
Users::LogStaffAction('[ Asset Moderation ] '.ucfirst($action).'d "'.$asset->name.'" [ID '.$asset->id.']'.($reason ? ' with reason: '.$reason : ''));
|
||||
api::respond(200, true, '"'.htmlspecialchars($asset->name).'" has been '.$action.'d');
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
api::initialize(["method" => "POST", "admin" => [Users::STAFF_MODERATOR, Users::STAFF_CATALOG, Users::STAFF_ADMINISTRATOR], "admin_ratelimit" => true, "secure" => true]);
|
||||
|
||||
if(!isset($_POST["username"]) || !isset($_POST["banType"]) || !isset($_POST["moderationNote"]) || !isset($_POST["until"]) || !isset($_POST["deleteUsername"])) api::respond(400, false, "Bad Request");
|
||||
if($_POST["banType"] < 1 || $_POST["banType"] > 4) api::respond(400, false, "Bad Request");
|
||||
if($_POST["banType"] != 4 && empty($_POST["moderationNote"])) api::respond(200, false, "You must supply a reason");
|
||||
if(!trim($_POST["username"])) api::respond(200, false, "You haven't set the username to ban");
|
||||
if($_POST["banType"] == 2 && empty($_POST["until"])) api::respond(200, false, "Ban time not set");
|
||||
|
||||
$banType = $_POST["banType"];
|
||||
$staffNote = isset($_POST["staffNote"]) && $_POST["staffNote"] ? $_POST["staffNote"] : "";
|
||||
$userId = SESSION["user"]["id"];
|
||||
$reason = $_POST["moderationNote"];
|
||||
$bannedUntil = $_POST["banType"] == 2 ? strtotime($_POST["until"]." ".date('G:i:s')) : 0;
|
||||
$deleteUsername = (int)($_POST["deleteUsername"] == "true");
|
||||
|
||||
if (strpos($_POST["username"], ",") === false)
|
||||
{
|
||||
$result = BanUser(Users::GetInfoFromName($_POST["username"]));
|
||||
if($result !== true) api::respond(200, false, $result);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (explode(",", $_POST["username"]) as $BannerID)
|
||||
{
|
||||
BanUser(Users::GetInfoFromID($BannerID));
|
||||
}
|
||||
}
|
||||
|
||||
function BanUser($bannerInfo)
|
||||
{
|
||||
global $banType, $staffNote, $userId, $reason, $bannedUntil, $deleteUsername;
|
||||
|
||||
if(!$bannerInfo) return "User does not exist";
|
||||
|
||||
if($banType == 4)
|
||||
{
|
||||
if(!Users::GetUserModeration($bannerInfo->id)) return "That user isn't banned!";
|
||||
Users::UndoUserModeration($bannerInfo->id, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if($bannerInfo->id == $userId) return "You cannot moderate yourself";
|
||||
if($bannerInfo->adminlevel > 0) return "You cannot moderate a staff member";
|
||||
if(Users::GetUserModeration($bannerInfo->id)) return "That user is already banned!";
|
||||
if($banType == 2 && $bannedUntil < strtotime('tomorrow')) return "Ban time must be at least 1 day long";
|
||||
|
||||
db::run(
|
||||
"INSERT INTO bans (userId, bannerId, timeStarted, timeEnds, reason, banType, note)
|
||||
VALUES (:bid, :uid, UNIX_TIMESTAMP(), :ends, :reason, :type, :note)",
|
||||
[":bid" => $bannerInfo->id, ":uid" => $userId, ":ends" => $bannedUntil, ":reason" => $reason, ":type" => $banType, ":note" => $staffNote]
|
||||
);
|
||||
}
|
||||
|
||||
if ($deleteUsername && $banType != 4)
|
||||
{
|
||||
db::run("UPDATE users SET username = :Username WHERE id = :UserID", [":Username" => "[ Content Deleted {$bannerInfo->id} ]", ":UserID" => $bannerInfo->id]);
|
||||
}
|
||||
|
||||
$staff =
|
||||
[
|
||||
1 => "Warned " . $bannerInfo->username,
|
||||
2 => "Banned " . $bannerInfo->username . " for " . GetReadableTime($bannedUntil, ["Ending" => false]),
|
||||
3 => "Permanently banned " . $bannerInfo->username,
|
||||
4 => "Unbanned " . $bannerInfo->username
|
||||
];
|
||||
|
||||
Users::LogStaffAction("[ User Moderation ] ".$staff[$banType]." ( user ID ".$bannerInfo->id." )");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$text =
|
||||
[
|
||||
1 => "warned",
|
||||
2 => "banned for " . GetReadableTime($bannedUntil, ["Ending" => false]),
|
||||
3 => "permanently banned",
|
||||
4 => "unbanned"
|
||||
];
|
||||
|
||||
api::respond(200, true, $_POST["username"]." has been ".$text[$banType]);
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
api::initialize(["method" => "POST", "admin" => [Users::STAFF_MODERATOR, Users::STAFF_CATALOG, Users::STAFF_ADMINISTRATOR], "secure" => true]);
|
||||
|
||||
if(!isset($_POST["banType"]) || !isset($_POST["moderationNote"]) || !isset($_POST["until"])){ api::respond(400, false, "Invalid Request"); }
|
||||
if($_POST["banType"] < 1 || $_POST["banType"] > 3){ api::respond(400, false, "Invalid Request"); }
|
||||
if(!trim($_POST["moderationNote"])){ api::respond(400, false, "You must supply a reason"); }
|
||||
if($_POST["banType"] == 2 && !trim($_POST["until"])){ api::respond(400, false, "Ban time not set"); }
|
||||
|
||||
$banType = $_POST["banType"];
|
||||
$bannedUntil = strtotime($_POST["until"]." ".date('G:i:s'));
|
||||
|
||||
if($bannedUntil < strtotime('tomorrow')){ api::respond(400, false, "Ban time must be at least 1 day long"); }
|
||||
|
||||
//markdown
|
||||
$markdown = new Parsedown();
|
||||
$markdown->setMarkupEscaped(true);
|
||||
$markdown->setBreaksEnabled(true);
|
||||
$markdown->setSafeMode(true);
|
||||
$markdown->setUrlsLinked(true);
|
||||
|
||||
$text =
|
||||
[
|
||||
"title" =>
|
||||
[
|
||||
1 => "Warning",
|
||||
2 => "Banned for ".timeSince("@".($bannedUntil+1), false, false),
|
||||
3 => "Account Deleted"
|
||||
],
|
||||
|
||||
"header" =>
|
||||
[
|
||||
1 => "This is just a heads-up to remind you to follow the rules",
|
||||
2 => "Your account has been banned for violating our rules",
|
||||
3 => "Your account has been permanently banned for violating our rules"
|
||||
],
|
||||
|
||||
"footer" =>
|
||||
[
|
||||
1 => "Please re-read the <a href='/info/rules'>rules</a> and abide by them to prevent yourself from facing a ban",
|
||||
2 => "Your ban ends at ".date('j/n/Y g:i:s A \G\M\T', $bannedUntil).", or in ".timeSince("@".($bannedUntil+1), true, false)." <br><br> Circumventing your ban on an alternate account while it is active may cause your ban time to be extended",
|
||||
3 => "Circumventing your ban by using an alternate account will lower your chance of appeal (if your ban was appealable) and potentially warrant you an IP ban"
|
||||
]
|
||||
];
|
||||
|
||||
ob_start(); ?>
|
||||
<h2 class="font-weight-normal"><?=$text["title"][$banType]?></h2>
|
||||
<p class="card-text"><?=$text["header"][$banType]?></p>
|
||||
<p class="card-text">Done at: <?=date('j/n/Y g:i:s A \G\M\T')?></p>
|
||||
<p class="card-text mb-0">Reason:</p>
|
||||
<div class="card">
|
||||
<div class="card-body p-2">
|
||||
<?=str_replace('<p>', '<p class="mb-0">', $markdown->text($_POST["moderationNote"], true))?>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<p class="card-text"><?=$text["footer"][$banType]?></p>
|
||||
<?php if($banType == 1) { ?>
|
||||
<a href="#" class="btn btn-primary disabled">Reactivate</a>
|
||||
<?php } api::respond(200, true, ob_get_clean());
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Catalog");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
Polygon::ImportClass("Image");
|
||||
Polygon::ImportLibrary("class.upload");
|
||||
|
||||
api::initialize(["method" => "POST", "admin" => Users::STAFF, "secure" => true]);
|
||||
|
||||
$renderType = $_POST['renderType'] ?? false;
|
||||
$assetID = $_POST['assetID'] ?? false;
|
||||
|
||||
if(!$renderType) api::respond(400, false, "Bad Request");
|
||||
if(!in_array($renderType, ["Avatar", "Asset"])) api::respond(400, false, "Invalid render type");
|
||||
if(!$assetID || !is_numeric($assetID)) api::respond(400, false, "Bad Request");
|
||||
|
||||
if($renderType == "Asset")
|
||||
{
|
||||
$asset = Catalog::GetAssetInfo($assetID);
|
||||
if(!$asset) api::respond(200, false, "The asset you requested does not exist");
|
||||
switch($asset->type)
|
||||
{
|
||||
case 9: Polygon::RequestRender("Place", $assetID); break; //place
|
||||
case 4: Polygon::RequestRender("Mesh", $assetID); break; // mesh
|
||||
case 8: case 19: Polygon::RequestRender("Model", $assetID); break; // hat/gear
|
||||
case 11: case 12: Polygon::RequestRender("Clothing", $assetID); break; // shirt/pants
|
||||
case 17: Polygon::RequestRender("Head", $assetID); break; // head
|
||||
case 10: Polygon::RequestRender("UserModel", $assetID); break; // user generated model
|
||||
case 2: // t-shirt
|
||||
$image = new Upload(SITE_CONFIG['paths']['assets'].$asset->imageID);
|
||||
|
||||
Thumbnails::UploadAsset($image, $asset->imageID, 420, 420, ["keepRatio" => true, "align" => "T"]);
|
||||
|
||||
//process initial tshirt thumbnail
|
||||
$template = imagecreatefrompng($_SERVER['DOCUMENT_ROOT']."/img/tshirt-template.png");
|
||||
$shirtdecal = Image::Resize(SITE_CONFIG['paths']['thumbs_assets']."{$asset->imageID}-420x420.png", 250, 250);
|
||||
imagesavealpha($template, true);
|
||||
imagesavealpha($shirtdecal, true);
|
||||
Image::MergeLayers($template, $shirtdecal, 85, 85, 0, 0, 250, 250, 100);
|
||||
|
||||
imagepng($template, SITE_CONFIG['paths']['thumbs_assets']."$assetID-420x420.png");
|
||||
Image::Resize(SITE_CONFIG['paths']['thumbs_assets']."$assetID-420x420.png", 100, 100, SITE_CONFIG['paths']['thumbs_assets']."$assetID-100x100.png");
|
||||
Image::Resize(SITE_CONFIG['paths']['thumbs_assets']."$assetID-420x420.png", 110, 110, SITE_CONFIG['paths']['thumbs_assets']."$assetID-110x110.png");
|
||||
|
||||
Thumbnails::UploadToCDN(SITE_CONFIG['paths']['thumbs_assets']."$assetID-100x100.png");
|
||||
Thumbnails::UploadToCDN(SITE_CONFIG['paths']['thumbs_assets']."$assetID-110x110.png");
|
||||
Thumbnails::UploadToCDN(SITE_CONFIG['paths']['thumbs_assets']."$assetID-420x420.png");
|
||||
break;
|
||||
case 13: // decal
|
||||
$image = new Upload(SITE_CONFIG['paths']['assets'].$asset->imageID);
|
||||
|
||||
Thumbnails::UploadAsset($image, $asset->imageID, 420, 420, ["keepRatio" => true, "align" => "C"]);
|
||||
Thumbnails::UploadAsset($image, $assetID, 420, 420);
|
||||
break;
|
||||
case 3: // audio
|
||||
Image::RenderFromStaticImage("audio", $assetID);
|
||||
break;
|
||||
default: api::respond(200, false, "This asset cannot be re-rendered");
|
||||
}
|
||||
}
|
||||
else if($renderType == "Avatar")
|
||||
{
|
||||
$user = Users::GetInfoFromID($assetID);
|
||||
if(!$user) api::respond(200, false, "The user you requested does not exist");
|
||||
Polygon::RequestRender("Avatar", $assetID);
|
||||
}
|
||||
|
||||
Users::LogStaffAction("[ Render ] Re-rendered $renderType ID $assetID");
|
||||
api::respond(200, true, "Render request has been successfully submitted! See render status <a href='/admin/render-queue'>here</a>");
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Catalog");
|
||||
Polygon::ImportClass("Image");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "admin" => [Users::STAFF_CATALOG, Users::STAFF_ADMINISTRATOR], "secure" => true]);
|
||||
|
||||
$file = $_FILES["file"] ?? false;
|
||||
$name = $_POST["name"] ?? "";
|
||||
$description = $_POST["description"] ?? "";
|
||||
$type = $_POST["type"] ?? false;
|
||||
$uploadas = $_POST["creator"] ?? "Polygon";
|
||||
$creator = Users::GetIDFromName($uploadas);
|
||||
|
||||
if(!$file) api::respond(200, false, "You must select a file");
|
||||
if(strlen($name) == 0) api::respond(200, false, "You must specify a name");
|
||||
if(strlen($name) > 50) api::respond(200, false, "Name cannot be longer than 50 characters");
|
||||
if(!$creator) api::respond(400, false, "The user you're trying to create as does not exist");
|
||||
if(Polygon::FilterText($name, false, false, true) != $name) api::respond(400, false, "The name contains inappropriate text");
|
||||
|
||||
//$lastCreation = $pdo->query("SELECT created FROM assets WHERE creator = 2 ORDER BY id DESC")->fetchColumn();
|
||||
//if($lastCreation+60 > time()) api::respond(400, false, "Please wait ".(60-(time()-$lastCreation))." seconds before creating a new asset");
|
||||
|
||||
if($type == 1) //image - this is for textures and stuff
|
||||
{
|
||||
if(!in_array($file["type"], ["image/png", "image/jpg", "image/jpeg"])) api::respond(400, false, "Must be a png or jpg file");
|
||||
|
||||
Polygon::ImportLibrary("class.upload");
|
||||
|
||||
$image = new Upload($file);
|
||||
if(!$image->uploaded) api::respond(500, false, "Failed to process image - please contact an admin");
|
||||
$image->allowed = ['image/png', 'image/jpg', 'image/jpeg'];
|
||||
$image->image_convert = 'png';
|
||||
|
||||
$imageId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => $description, "approved" => 1]);
|
||||
Image::Process($image, ["name" => "$imageId", "resize" => false, "dir" => "assets/"]);
|
||||
Thumbnails::UploadAsset($image, $imageId, 420, 420, ["keepRatio" => true, "align" => "C"]);
|
||||
}
|
||||
elseif($type == 3) // audio
|
||||
{
|
||||
if(!in_array($file["type"], ["audio/mpeg", "audio/ogg", "audio/mid", "audio/wav"])) api::respond(400, false, "Must be an mpeg, wav, ogg or midi audio. - ".$file["type"]);
|
||||
$assetId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => $description, "audioType" => $file["type"], "approved" => 1]);
|
||||
copy($file["tmp_name"], SITE_CONFIG["paths"]["assets"] . $assetId);
|
||||
Image::RenderFromStaticImage("audio", $assetId);
|
||||
}
|
||||
elseif($type == 4) //mesh
|
||||
{
|
||||
if(!str_ends_with($file["name"], ".mesh")) api::respond(400, false, "Must be a .mesh file");
|
||||
$assetId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => $description, "approved" => 1]);
|
||||
copy($file["tmp_name"], SITE_CONFIG["paths"]["assets"] . $assetId);
|
||||
Polygon::RequestRender("Mesh", $assetId);
|
||||
}
|
||||
elseif($type == 5) //lua
|
||||
{
|
||||
if(!str_ends_with($file["name"], ".lua")) api::respond(400, false, "Must be a .lua file");
|
||||
$assetId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => $description, "approved" => 1]);
|
||||
copy($file["tmp_name"], SITE_CONFIG["paths"]["assets"] . $assetId);
|
||||
Image::RenderFromStaticImage("Script", $assetId);
|
||||
}
|
||||
elseif($type == 8) //hat
|
||||
{
|
||||
if(!str_ends_with($file["name"], ".xml") && !str_ends_with($file["name"], ".rbxm")) api::respond(400, false, "Must be an rbxm or xml file");
|
||||
$assetId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => $description, "approved" => 1]);
|
||||
copy($file["tmp_name"], SITE_CONFIG["paths"]["assets"] . $assetId);
|
||||
Polygon::RequestRender("Model", $assetId);
|
||||
}
|
||||
elseif($type == 17) //head
|
||||
{
|
||||
if(!str_ends_with($file["name"], ".xml") && !str_ends_with($file["name"], ".rbxm")) api::respond(400, false, "Must be an rbxm or xml file");
|
||||
$assetId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => $description, "approved" => 1]);
|
||||
copy($file["tmp_name"], SITE_CONFIG["paths"]["assets"] . $assetId);
|
||||
Polygon::RequestRender("Head", $assetId);
|
||||
}
|
||||
elseif($type == 18) //faces are literally just decals lmao (with a minor alteration to the xml)
|
||||
{
|
||||
if(!in_array($file["type"], ["image/png", "image/jpg", "image/jpeg"])) api::respond(400, false, "Must be a png or jpg file");
|
||||
|
||||
Polygon::ImportLibrary("class.upload");
|
||||
|
||||
$image = new Upload($file);
|
||||
if(!$image->uploaded) api::respond(500, false, "Failed to process image - please contact an admin");
|
||||
$image->allowed = ['image/png', 'image/jpg', 'image/jpeg'];
|
||||
$image->image_convert = 'png';
|
||||
|
||||
$imageId = Catalog::CreateAsset(["type" => 1, "creator" => $creator, "name" => $name, "description" => $description, "approved" => 1]);
|
||||
Image::Process($image, ["name" => "$imageId", "resize" => false, "dir" => "assets/"]);
|
||||
Thumbnails::UploadAsset($image, $imageId, 420, 420, ["keepRatio" => true, "align" => "C"]);
|
||||
|
||||
$itemId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => $description, "imageID" => $imageId, "approved" => 1]);
|
||||
|
||||
file_put_contents(SITE_CONFIG['paths']['assets'].$itemId, Catalog::GenerateGraphicXML("Face", $imageId));
|
||||
|
||||
Thumbnails::UploadAsset($image, $itemId, 420, 420);
|
||||
}
|
||||
elseif($type == 19) //gear
|
||||
{
|
||||
if(!str_ends_with($file["name"], ".xml") && !str_ends_with($file["name"], ".rbxm")) api::respond(400, false, "Must be an rbxm or xml file");
|
||||
|
||||
$assetId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => $description, "approved" => 1, "gear_attributes" => '{"melee":false,"powerup":false,"ranged":false,"navigation":false,"explosive":false,"musical":false,"social":false,"transport":false,"building":false}']);
|
||||
copy($file["tmp_name"], SITE_CONFIG["paths"]["assets"] . $assetId);
|
||||
Polygon::RequestRender("Model", $assetId);
|
||||
}
|
||||
else if ($type == 24) // animation
|
||||
{
|
||||
if(!str_ends_with($file["name"], ".xml") && !str_ends_with($file["name"], ".rbxm")) api::respond(400, false, "Must be an rbxm or xml file");
|
||||
|
||||
$assetId = Catalog::CreateAsset(["type" => $type, "creator" => $creator, "name" => $name, "description" => $description, "approved" => 1]);
|
||||
copy($file["tmp_name"], SITE_CONFIG["paths"]["assets"] . $assetId);
|
||||
Image::RenderFromStaticImage("Animation", $assetId);
|
||||
}
|
||||
|
||||
Users::LogStaffAction("[ Asset creation ] Created \"$name\" [ID ".($itemId ?? $assetId ?? $imageId)."]");
|
||||
api::respond_custom([
|
||||
"status" => 200,
|
||||
"success" => true,
|
||||
"message" => "<a href='/item?ID=".($itemId ?? $assetId ?? $imageId)."'>".Catalog::GetTypeByNum($type)."</a> successfully created!",
|
||||
"assetID" => ($itemId ?? $assetId ?? $imageId)
|
||||
]);
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize();
|
||||
|
||||
$AssetID = api::GetParameter("GET", "assetID", "int");
|
||||
$Page = api::GetParameter("GET", "page", "int", 1);
|
||||
|
||||
$CommentsCount = db::run("SELECT COUNT(*) FROM asset_comments WHERE assetID = :AssetID", [":AssetID" => $AssetID])->fetchColumn();
|
||||
if($CommentsCount == 0) api::respond(200, true, "This item does not have any comments");
|
||||
|
||||
$Pagination = Pagination($Page, $CommentsCount, 15);
|
||||
|
||||
$Comments = db::run(
|
||||
"SELECT asset_comments.*, users.username FROM asset_comments
|
||||
INNER JOIN users ON users.id = asset_comments.author
|
||||
WHERE assetID = :AssetID
|
||||
ORDER BY id DESC LIMIT 15 OFFSET :Offset",
|
||||
[":AssetID" => $AssetID, ":Offset" => $Pagination->Offset]
|
||||
);
|
||||
|
||||
$Items = [];
|
||||
|
||||
while($Comment = $Comments->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$Items[] =
|
||||
[
|
||||
"time" => strtolower(timeSince($Comment->time)),
|
||||
"commenter_name" => $Comment->username,
|
||||
"commenter_id" => $Comment->author,
|
||||
"commenter_avatar" => Thumbnails::GetAvatar($Comment->author),
|
||||
"content" => nl2br(Polygon::FilterText($Comment->content))
|
||||
];
|
||||
}
|
||||
|
||||
api::respond_custom(["status" => 200, "success" => true, "message" => "OK", "items" => $Items, "pages" => $Pagination->Pages]);
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Catalog");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
if(!isset($_POST['assetID']) || !isset($_POST['content']));
|
||||
|
||||
$uid = SESSION["user"]["id"];
|
||||
$id = $_POST['assetID'];
|
||||
$content = $_POST['content'];
|
||||
|
||||
$item = Catalog::GetAssetInfo($id);
|
||||
if(!$item) api::respond(400, false, "Asset does not exist");
|
||||
if(!$item->comments) api::respond(400, false, "Comments are unavailable for this asset");
|
||||
if(!strlen($content)) api::respond(400, false, "Comment cannot be empty");
|
||||
if(strlen($content) > 100) api::respond(400, false, "Comment cannot be longer than 128 characters");
|
||||
|
||||
$query = $pdo->prepare("SELECT time FROM asset_comments WHERE time+60 > UNIX_TIMESTAMP() AND author = :uid");
|
||||
$query->bindParam(":uid", $uid, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
if($query->rowCount()) api::respond(400, false, "Please wait ".GetReadableTime($query->fetchColumn(), ["RelativeTime" => "1 minute"])." before posting a new comment");
|
||||
|
||||
$query = $pdo->prepare("INSERT INTO asset_comments (author, content, assetID, time) VALUES (:uid, :content, :aid, UNIX_TIMESTAMP())");
|
||||
$query->bindParam(":uid", $uid, PDO::PARAM_INT);
|
||||
$query->bindParam(":content", $content, PDO::PARAM_STR);
|
||||
$query->bindParam(":aid", $id, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
|
||||
api::respond(200, true, "OK");
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Catalog");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
function getPrice($price)
|
||||
{
|
||||
return $price ? '<span class="text-success"><i class="fal fa-pizza-slice"></i> '.$price.'</span>' : '<span class="text-success">Free</span>';
|
||||
}
|
||||
|
||||
$uid = SESSION["user"]["id"];
|
||||
$id = $_POST['id'] ?? false;
|
||||
$price = $_POST['price'] ?? 0;
|
||||
|
||||
$item = Catalog::GetAssetInfo($id);
|
||||
if(!$item) api::respond(400, false, "Asset does not exist");
|
||||
if(Catalog::OwnsAsset($uid, $id)) api::respond(400, false, "User already owns asset");
|
||||
if(!$item->sale) api::respond(400, false, "Asset is off-sale");
|
||||
if(SESSION["user"]["currency"] - $item->price < 0) api::respond(400, false, "User cannot afford asset");
|
||||
|
||||
if($item->price != $price)
|
||||
{
|
||||
die(json_encode(
|
||||
[
|
||||
"status" => 200,
|
||||
"success" => true,
|
||||
"message" => "Item price changed",
|
||||
"header" => "Item Price Has Changed",
|
||||
"text" => 'While you were shopping, the price of this item changed from '.getPrice($price).' to '.getPrice($item->price).'.',
|
||||
"buttons" => [['class'=>'btn btn-success btn-confirm-purchase', 'text'=>'Buy Now'], ['class'=>'btn btn-secondary', 'dismiss'=>true, 'text'=>'Cancel']],
|
||||
"footer" => 'Your balance after this transaction will be <i class="fal fa-pizza-slice"></i> '.(SESSION["user"]["currency"] - $item->price),
|
||||
"newprice" => $item->price
|
||||
]));
|
||||
}
|
||||
|
||||
$IsAlt = false;
|
||||
|
||||
foreach(Users::GetAlternateAccounts($item->creator) as $alt)
|
||||
{
|
||||
if($alt["userid"] == $uid) $IsAlt = true;
|
||||
}
|
||||
|
||||
if(!$IsAlt)
|
||||
{
|
||||
$query = $pdo->prepare("UPDATE users SET currency = currency - :price WHERE id = :uid; UPDATE users SET currency = currency + :price WHERE id = :seller");
|
||||
$query->bindParam(":price", $item->price, PDO::PARAM_INT);
|
||||
$query->bindParam(":uid", $uid, PDO::PARAM_INT);
|
||||
$query->bindParam(":seller", $item->creator, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
$query = $pdo->prepare("INSERT INTO ownedAssets (assetId, userId, timestamp) VALUES (:aid, :uid, UNIX_TIMESTAMP())");
|
||||
$query->bindParam(":aid", $id, PDO::PARAM_INT);
|
||||
$query->bindParam(":uid", $uid, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
|
||||
$query = $pdo->prepare("INSERT INTO transactions (purchaser, seller, assetId, amount, flagged, timestamp) VALUES (:uid, :sid, :aid, :price, :flagged, UNIX_TIMESTAMP())");
|
||||
$query->bindParam(":uid", $uid, PDO::PARAM_INT);
|
||||
$query->bindParam(":sid", $item->creator, PDO::PARAM_INT);
|
||||
$query->bindParam(":aid", $id, PDO::PARAM_INT);
|
||||
$query->bindParam(":price", $item->price, PDO::PARAM_INT);
|
||||
$query->bindParam(":flagged", $IsAlt, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
|
||||
if(time() < strtotime("2021-09-07 00:00:00") && $id == 2692 && !Catalog::OwnsAsset(SESSION["user"]["id"], 2800))
|
||||
{
|
||||
db::run(
|
||||
"INSERT INTO ownedAssets (assetId, userId, timestamp) VALUES (2800, :uid, UNIX_TIMESTAMP())",
|
||||
[":uid" => SESSION["user"]["id"]]
|
||||
);
|
||||
}
|
||||
|
||||
die(json_encode(
|
||||
[
|
||||
"status" => 200,
|
||||
"success" => true,
|
||||
"message" => "OK",
|
||||
"header" => "Purchase Complete!",
|
||||
"image" => Thumbnails::GetAsset($item),
|
||||
"text" => "You have successfully purchased the ".htmlspecialchars($item->name)." ".Catalog::GetTypeByNum($item->type)." from ".$item->username." for ".getPrice($item->price),
|
||||
"buttons" => [['class' => 'btn btn-primary continue-shopping', 'dismiss' => true, 'text' => 'Continue Shopping']],
|
||||
]));
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Catalog");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$userid = SESSION["user"]["id"];
|
||||
$type = $_POST["type"] ?? false;
|
||||
$page = $_POST["page"] ?? 1;
|
||||
$assets = [];
|
||||
|
||||
if(!Catalog::GetTypeByNum($type)) api::respond(400, false, "Invalid asset type");
|
||||
|
||||
$query = $pdo->prepare("SELECT * FROM assets WHERE creator = :uid AND type = :type ORDER BY id DESC");
|
||||
$query->bindParam(":uid", $userid, PDO::PARAM_INT);
|
||||
$query->bindParam(":type", $type, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
|
||||
while($asset = $query->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$info = Catalog::GetAssetInfo($asset->id);
|
||||
|
||||
$assets[] =
|
||||
[
|
||||
"name" => htmlspecialchars($asset->name),
|
||||
"id" => $asset->id,
|
||||
"version" => $asset->type == 9 ? $asset->Version : false,
|
||||
"thumbnail" => Thumbnails::GetAsset($asset),
|
||||
"item_url" => "/".encode_asset_name($asset->name)."-item?id={$asset->id}",
|
||||
"config_url" => $asset->type == 9 ? "/places/{$asset->id}/update" : "/my/item?ID={$asset->id}",
|
||||
"created" => date("n/j/Y", $asset->created),
|
||||
"sales-total" => $info->sales_total,
|
||||
"sales-week" => $info->sales_week
|
||||
];
|
||||
}
|
||||
|
||||
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "assets" => $assets]));
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Catalog");
|
||||
Polygon::ImportClass("Image");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$userid = SESSION["user"]["id"];
|
||||
$file = $_FILES["file"] ?? false;
|
||||
$name = $_POST["name"] ?? false;
|
||||
$type = $_POST["type"] ?? false;
|
||||
|
||||
if(!$file) api::respond(200, false, "You must select a file");
|
||||
if(!in_array($file["type"], ["image/png", "image/jpg", "image/jpeg"])) api::respond(200, false, "Must be a .png or .jpg file");
|
||||
|
||||
if(empty($name)) api::respond(200, false, "You must specify a name");
|
||||
if(strlen($name) > 50) api::respond(200, false, "The name is too long");
|
||||
if(Polygon::IsExplicitlyFiltered($name)) api::respond(200, false, "The name contains inappropriate text");
|
||||
|
||||
if(!in_array($type, [2, 11, 12, 13])) api::respond(200, false, "You can't upload that type of content!");
|
||||
|
||||
$query = $pdo->prepare("SELECT created FROM assets WHERE creator = :uid ORDER BY id DESC");
|
||||
$query->bindParam(":uid", $userid, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
$lastCreation = $query->fetchColumn();
|
||||
if($userid != 1 && $lastCreation+30 > time()) api::respond(200, false, "Please wait ".(30-(time()-$lastCreation))." seconds before creating a new asset");
|
||||
|
||||
// tshirts are a bit messy but straightforward:
|
||||
// the image asset itself must be 128x128 with the texture resized to preserve aspect ratio
|
||||
// the image thumbnail should have the texture positioned top
|
||||
//
|
||||
// shirts and pants should ideally be 585x559 but it doesnt really matter -
|
||||
// just as long as it looks right on the avatar. if it doesnt then disapprove
|
||||
//
|
||||
// decals are a lot more messy:
|
||||
// the image asset itself is scaled to be 256 pixels in width, while preserving the texture ratio
|
||||
// the image thumbnail should have the texture positioned center
|
||||
// the decal asset however must have the texture stretched to 1:1 for all its respective sizes
|
||||
// [example: https://www.roblox.com/Item.aspx?ID=8553820]
|
||||
//
|
||||
// we won't have to worry about image size constraints as they're always gonna be
|
||||
// resized to fit in a smaller resolution
|
||||
//
|
||||
// refer to here for the thumbnail sizes: https://github.com/matthewdean/roblox-web-apis
|
||||
//
|
||||
// THUMBNAIL SIZES FOR EACH ITEM TYPE
|
||||
// legend: [f = fit] [t = top] [c = center] [s = stretch] // [M = Model] [He = Head] [S = Shirt] [P = Pants]
|
||||
//
|
||||
// | 48x48 | 60x62 | 75x75 | 100x100 | 110x110 | 160x100 | 250x250 | 352x352 | 420x230 | 420x420 |
|
||||
// +-------+-------+-------+---------+---------+---------+---------+---------+---------+---------+
|
||||
// Image | |yes (f)| | | | | | | | yes (t) |
|
||||
// +-------+-------+-------+---------+---------+---------+---------+---------+---------+---------+
|
||||
// T-Shirt | | | | yes | yes | | | | | yes |
|
||||
// +-------+-------+-------+---------+---------+---------+---------+---------+---------+---------+
|
||||
// Audio | | | yes | yes | yes | | yes | yes | yes | yes |
|
||||
// +-------+-------+-------+---------+---------+---------+---------+---------+---------+---------+
|
||||
// Hat/Gear | yes | | yes | yes | yes | | yes | yes | yes (fc)| yes |
|
||||
// +-------+-------+-------+---------+---------+---------+---------+---------+---------+---------+
|
||||
// Place | yes |yes(fc)| yes | yes | yes | yes | yes | yes | yes | yes |
|
||||
// +-------+-------+-------+---------+---------+---------+---------+---------+---------+---------+
|
||||
// M/He/S/P | yes | | yes | yes | yes | | yes | yes | | yes |
|
||||
// +-------+-------+-------+---------+---------+---------+---------+---------+---------+---------+
|
||||
// Decal/Face |yes (s)| |yes (s)| yes (s) | yes (s) | | yes (s) | yes (s) | yes (s) | yes (s) |
|
||||
// +-------+-------+-------+---------+---------+---------+---------+---------+---------+---------+
|
||||
|
||||
Polygon::ImportLibrary("class.upload");
|
||||
|
||||
$image = new Upload($file);
|
||||
if(!$image->uploaded) api::respond(200, false, "Failed to process image - please contact an admin");
|
||||
$image->allowed = ['image/png', 'image/jpg', 'image/jpeg'];
|
||||
$image->image_convert = 'png';
|
||||
|
||||
$imageId = Catalog::CreateAsset(["type" => 1, "creator" => SESSION["user"]["id"], "name" => $name, "description" => Catalog::GetTypeByNum($type)." Image"]);
|
||||
|
||||
if($type == 2) //tshirt
|
||||
{
|
||||
$Processed = Image::Process($image, ["name" => "$imageId", "keepRatio" => true, "align" => "T", "x" => 128, "y" => 128, "dir" => "assets/"]);
|
||||
if ($Processed !== true) api::respond(200, false, "Image processing failed: $Processed");
|
||||
|
||||
Thumbnails::UploadAsset($image, $imageId, 420, 420, ["keepRatio" => true, "align" => "T"]);
|
||||
|
||||
$itemId = Catalog::CreateAsset(["type" => 2, "creator" => SESSION["user"]["id"], "name" => $name, "description" => "T-Shirt", "imageID" => $imageId]);
|
||||
|
||||
file_put_contents(SITE_CONFIG['paths']['assets'].$itemId, Catalog::GenerateGraphicXML("T-Shirt", $imageId));
|
||||
|
||||
//process initial tshirt thumbnail
|
||||
$template = imagecreatefrompng($_SERVER['DOCUMENT_ROOT']."/img/tshirt-template.png");
|
||||
$shirtdecal = Image::Resize(SITE_CONFIG['paths']['thumbs_assets']."/$imageId-420x420.png", 250, 250);
|
||||
imagesavealpha($template, true);
|
||||
imagesavealpha($shirtdecal, true);
|
||||
Image::MergeLayers($template, $shirtdecal, 85, 85, 0, 0, 250, 250, 100);
|
||||
|
||||
imagepng($template, SITE_CONFIG['paths']['thumbs_assets']."/$itemId-420x420.png");
|
||||
Image::Resize(SITE_CONFIG['paths']['thumbs_assets']."/$itemId-420x420.png", 100, 100, SITE_CONFIG['paths']['thumbs_assets']."/$itemId-100x100.png");
|
||||
Image::Resize(SITE_CONFIG['paths']['thumbs_assets']."/$itemId-420x420.png", 110, 110, SITE_CONFIG['paths']['thumbs_assets']."/$itemId-110x110.png");
|
||||
|
||||
Thumbnails::UploadToCDN(SITE_CONFIG['paths']['thumbs_assets']."/$itemId-100x100.png");
|
||||
Thumbnails::UploadToCDN(SITE_CONFIG['paths']['thumbs_assets']."/$itemId-110x110.png");
|
||||
Thumbnails::UploadToCDN(SITE_CONFIG['paths']['thumbs_assets']."/$itemId-420x420.png");
|
||||
}
|
||||
elseif($type == 11 || $type == 12) //shirt / pants
|
||||
{
|
||||
$Processed = Image::Process($image, ["name" => "$imageId", "x" => 585, "y" => 559, "dir" => "assets/"]);
|
||||
if ($Processed !== true) api::respond(200, false, "Image processing failed: $Processed");
|
||||
|
||||
Thumbnails::UploadAsset($image, $imageId, 420, 420, ["keepRatio" => true, "align" => "C"]);
|
||||
|
||||
$itemId = Catalog::CreateAsset(["type" => $type, "creator" => SESSION["user"]["id"], "name" => $name, "description" => Catalog::GetTypeByNum($type), "imageID" => $imageId]);
|
||||
file_put_contents(SITE_CONFIG['paths']['assets'].$itemId, Catalog::GenerateGraphicXML(Catalog::GetTypeByNum($type), $imageId));
|
||||
Polygon::RequestRender("Clothing", $itemId);
|
||||
}
|
||||
elseif($type == 13) //decal
|
||||
{
|
||||
$Processed = Image::Process($image, ["name" => "$imageId", "x" => 256, "scaleY" => true, "dir" => "assets/"]);
|
||||
if ($Processed !== true) api::respond(200, false, "Image processing failed: $Processed");
|
||||
|
||||
Thumbnails::UploadAsset($image, $imageId, 420, 420, ["keepRatio" => true, "align" => "C"]);
|
||||
|
||||
$itemId = Catalog::CreateAsset(["type" => 13, "creator" => SESSION["user"]["id"], "name" => $name, "description" => "Decal", "imageID" => $imageId]);
|
||||
|
||||
file_put_contents(SITE_CONFIG['paths']['assets'].$itemId, Catalog::GenerateGraphicXML("Decal", $imageId));
|
||||
Thumbnails::UploadAsset($image, $itemId, 420, 420);
|
||||
}
|
||||
|
||||
api::respond(200, true, Catalog::GetTypeByNum($type)." successfully created!");
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
require $_SERVER["DOCUMENT_ROOT"]."/api/private/core.php";
|
||||
api::initialize(["method" => "GET", "api" => "DiscordBot"]);
|
||||
|
||||
if (isset($_GET["Token"]) && isset($_GET["DiscordID"]))
|
||||
{
|
||||
$userInfo = db::run("SELECT * FROM users WHERE discordKey = :key", [":key" => $_GET["Token"]])->fetch(PDO::FETCH_OBJ);
|
||||
if (!$userInfo) api::respond(200, false, "InvalidKey"); // check if verification key is valid
|
||||
if ($userInfo->discordID != NULL) api::respond(200, false, "AlreadyVerified"); // check if mercury account is already verified
|
||||
|
||||
db::run(
|
||||
"UPDATE users SET discordID = :id, discordVerifiedTime = UNIX_TIMESTAMP() WHERE discordKey = :key",
|
||||
[":id" => $_GET["DiscordID"], ":key" => $_GET["Token"]]
|
||||
);
|
||||
|
||||
api::respond(200, true, $userInfo->username);
|
||||
}
|
||||
else if (isset($_GET["DiscordID"]))
|
||||
{
|
||||
$username = db::run("SELECT username FROM users WHERE discordID = :id", [":id" => $_GET["DiscordID"]]);
|
||||
if (!$username->rowCount()) api::respond(200, false, "NotVerified"); // check if discord account is already verified
|
||||
api::respond(200, true, $username->fetchColumn());
|
||||
}
|
||||
|
||||
api::respond(400, false, "Bad Request");
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?php require $_SERVER["DOCUMENT_ROOT"]."/api/private/core.php";
|
||||
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "GET", "api" => "DiscordBot"]);
|
||||
|
||||
if (isset($_GET["UserName"]))
|
||||
{
|
||||
$userInfo = db::run(
|
||||
"SELECT id, username, blurb, adminlevel, jointime, lastonline, discordID, discordVerifiedTime FROM users WHERE username = :name",
|
||||
[":name" => $_GET["UserName"]]
|
||||
)->fetch(PDO::FETCH_OBJ);
|
||||
if (!$userInfo) api::respond(200, false, "DoesntExist");
|
||||
}
|
||||
else if (isset($_GET["DiscordID"]))
|
||||
{
|
||||
$userInfo = db::run(
|
||||
"SELECT id, username, blurb, adminlevel, jointime, lastonline, discordID, discordVerifiedTime FROM users WHERE discordID = :id",
|
||||
[":id" => $_GET["DiscordID"]]
|
||||
)->fetch(PDO::FETCH_OBJ);
|
||||
if (!$userInfo) api::respond(200, false, "NotVerified");
|
||||
}
|
||||
else
|
||||
{
|
||||
api::respond(400, false, "Bad Request");
|
||||
}
|
||||
|
||||
$userInfo->thumbnail = Thumbnails::GetAvatar($userInfo->id, 420, 420, true);
|
||||
|
||||
$userInfo->blurb = str_ireplace(["@everyone", "@here"], ["[everyone]", "[here]"], $userInfo->blurb);
|
||||
$userInfo->blurb = preg_replace("/<(@[0-9]+)>/i", "[$1]", $userInfo->blurb);
|
||||
api::respond(200, true, $userInfo);
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$RequestsCount = db::run(
|
||||
"SELECT COUNT(*) FROM friends WHERE receiverId = :UserID AND status = 0", [":UserID" => SESSION["user"]["id"]]
|
||||
)->fetchColumn();
|
||||
|
||||
if($RequestsCount == 0) api::respond(200, false, "You don't have any friend requests to accept right now");
|
||||
|
||||
db::run("UPDATE friends SET status = 1 WHERE receiverId = :UserID AND status = 0", [":UserID" => SESSION["user"]["id"]]);
|
||||
|
||||
api::respond(200, true, "All your friend requests have been accepted");
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$FriendID = api::GetParameter("POST", "FriendID", "int");
|
||||
|
||||
$FriendRequest = db::run("SELECT * FROM friends WHERE id = :FriendID AND status = 0", [":FriendID" => $FriendID]);
|
||||
$FriendRequestInfo = $FriendRequest->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
if($FriendRequest->rowCount() == 0) api::respond(200, false, "Friend request doesn't exist");
|
||||
if((int) $FriendRequestInfo->receiverId != SESSION["user"]["id"]) api::respond(200, false, "You are not the recipient of this friend request");
|
||||
|
||||
db::run("UPDATE friends SET status = 1 WHERE id = :FriendID", [":FriendID" => $FriendID]);
|
||||
|
||||
api::respond(200, true, "OK");
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$Page = api::GetParameter("POST", "Page", "int", 1);
|
||||
|
||||
$RequestsCount = db::run(
|
||||
"SELECT COUNT(*) FROM friends WHERE receiverId = :UserID AND status = 0",
|
||||
[":UserID" => SESSION["user"]["id"]]
|
||||
)->fetchColumn();
|
||||
if($RequestsCount == 0) api::respond(200, true, "You're all up-to-date with your friend requests");
|
||||
|
||||
$Pagination = Pagination($Page, $RequestsCount, 18);
|
||||
|
||||
$Requests = db::run(
|
||||
"SELECT * FROM friends WHERE receiverId = :UserID AND status = 0 LIMIT 18 OFFSET :Offset",
|
||||
[":UserID" => SESSION["user"]["id"], ":Offset" => $Pagination->Offset]
|
||||
);
|
||||
|
||||
while($Request = $Requests->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$Items[] =
|
||||
[
|
||||
"Username" => Users::GetNameFromID($Request->requesterId),
|
||||
"UserID" => $Request->requesterId,
|
||||
"Avatar" => Thumbnails::GetAvatar($Request->requesterId),
|
||||
"FriendID" => $Request->id
|
||||
];
|
||||
}
|
||||
|
||||
api::respond_custom(["status" => 200, "success" => true, "message" => "OK", "items" => $Items, "count" => (int) $RequestsCount, "pages" => (int) $Pagination->Pages]);
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$userid = SESSION["user"]["id"];
|
||||
$page = $_POST['page'] ?? 1;
|
||||
|
||||
$query = $pdo->prepare("SELECT COUNT(*) FROM friends WHERE receiverId = :uid AND status = 0");
|
||||
$query->bindParam(":uid", $userid, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
|
||||
$pages = ceil($query->fetchColumn()/18);
|
||||
$offset = ($page - 1)*18;
|
||||
|
||||
if(!$pages) api::respond(200, true, "You're all up-to-date with your friend requests!");
|
||||
|
||||
$query = $pdo->prepare("SELECT * FROM friends WHERE receiverId = :uid AND status = 0 LIMIT 18 OFFSET :offset");
|
||||
$query->bindParam(":uid", $userid, PDO::PARAM_INT);
|
||||
$query->bindParam(":offset", $offset, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
|
||||
$friends = [];
|
||||
|
||||
while($row = $query->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$friends[] =
|
||||
[
|
||||
"username" => Users::GetNameFromID($row->requesterId),
|
||||
"userid" => $row->requesterId,
|
||||
"avatar" => Thumbnails::GetAvatar($row->requesterId),
|
||||
"friendid" => $row->id
|
||||
];
|
||||
}
|
||||
|
||||
api::respond_custom(["status" => 200, "success" => true, "message" => "OK", "requests" => $friends, "pages" => $pages]);
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST"]);
|
||||
|
||||
$url = $_SERVER['HTTP_REFERER'] ?? false;
|
||||
$userId = $_POST['userID'] ?? false;
|
||||
$page = $_POST['page'] ?? 1;
|
||||
$order = strpos($url, "/home") ? "lastonline DESC" : "id";
|
||||
$limit = strpos($url, "/friends") ? 18 : 6;
|
||||
$self = str_ends_with($url, "/user") || str_ends_with($url, "/friends") || strpos($url, "/home");
|
||||
|
||||
if(!Users::GetInfoFromID($userId)) api::respond(400, false, "User does not exist");
|
||||
|
||||
$query = $pdo->prepare("SELECT COUNT(*) FROM friends WHERE :uid IN (requesterId, receiverId) AND status = 1");
|
||||
$query->bindParam(":uid", $userId, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
|
||||
$pages = ceil($query->fetchColumn()/$limit);
|
||||
$offset = ($page - 1)*$limit;
|
||||
|
||||
if(!$pages) api::respond(200, true, ($self ? "You do" : Users::GetNameFromID($userId)." does")."n't have any friends");
|
||||
|
||||
$query = $pdo->prepare("
|
||||
SELECT friends.*, users.username, users.id AS userId, users.status, users.lastonline FROM friends
|
||||
INNER JOIN users ON users.id = (CASE WHEN requesterId = :uid THEN receiverId ELSE requesterId END)
|
||||
WHERE :uid IN (requesterId, receiverId) AND friends.status = 1
|
||||
ORDER BY $order LIMIT :limit OFFSET :offset");
|
||||
$query->bindParam(":uid", $userId, PDO::PARAM_INT);
|
||||
$query->bindParam(":limit", $limit, PDO::PARAM_INT);
|
||||
$query->bindParam(":offset", $offset, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
|
||||
$friends = [];
|
||||
|
||||
while($row = $query->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$friends[] =
|
||||
[
|
||||
"username" => $row->username,
|
||||
"userid" => $row->userId,
|
||||
"avatar" => Thumbnails::GetAvatar($row->userId),
|
||||
"friendid" => $row->id,
|
||||
"status" => Polygon::FilterText($row->status)
|
||||
];
|
||||
}
|
||||
|
||||
api::respond_custom(["status" => 200, "success" => true, "message" => "OK", "items" => $friends, "pages" => $pages]);
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$RequestsCount = db::run(
|
||||
"SELECT COUNT(*) FROM friends WHERE receiverId = :UserID AND status = 0", [":UserID" => SESSION["user"]["id"]]
|
||||
)->fetchColumn();
|
||||
|
||||
if($RequestsCount == 0) api::respond(200, false, "You don't have any friend requests to decline right now");
|
||||
|
||||
db::run("UPDATE friends SET status = 2 WHERE receiverId = :UserID AND status = 0", [":UserID" => SESSION["user"]["id"]]);
|
||||
|
||||
api::respond(200, true, "All your friend requests have been decline");
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$FriendID = api::GetParameter("POST", "FriendID", "int");
|
||||
|
||||
$FriendConnection = db::run("SELECT * FROM friends WHERE id = :FriendID AND NOT status = 2", [":FriendID" => $FriendID]);
|
||||
$FriendConnectionInfo = $FriendConnection->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
if($FriendConnection->rowCount() == 0) api::respond(200, false, "Friend connection doesn't exist");
|
||||
if(!in_array(SESSION["user"]["id"], [$FriendConnectionInfo->requesterId, $FriendConnectionInfo->receiverId])) api::respond(200, false, "You are not a part of this friend connection");
|
||||
|
||||
db::run("UPDATE friends SET status = 2 WHERE id = :FriendID", [":FriendID" => $FriendID]);
|
||||
|
||||
api::respond(200, true, "OK");
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$UserID = api::GetParameter("POST", "UserID", "int");
|
||||
|
||||
if($UserID == SESSION["user"]["id"]) api::respond(200, false, "You can't perform friend operations on yourself");
|
||||
|
||||
$FriendConnection = db::run(
|
||||
"SELECT status FROM friends WHERE :UserID IN (requesterId, receiverId) AND :ReceiverID IN (requesterId, receiverId) AND NOT status = 2",
|
||||
[":UserID" => SESSION["user"]["id"], ":ReceiverID" => $UserID]
|
||||
);
|
||||
if($FriendConnection->rowCount() != 0) api::respond(200, false, "Friend connection already exists");
|
||||
|
||||
$LastRequest = db::run(
|
||||
"SELECT timeSent FROM friends WHERE requesterId = :UserID AND timeSent+60 > UNIX_TIMESTAMP()",
|
||||
[":UserID" => SESSION["user"]["id"]]
|
||||
);
|
||||
if($LastRequest->rowCount() != 0) api::respond(200, false, "Please wait ".GetReadableTime($LastRequest->fetchColumn(), ["RelativeTime" => "1 minute"])." before sending another request");
|
||||
|
||||
db::run(
|
||||
"INSERT INTO friends (requesterId, receiverId, timeSent) VALUES (:UserID, :ReceiverID, UNIX_TIMESTAMP())",
|
||||
[":UserID" => SESSION["user"]["id"], ":ReceiverID" => $UserID]
|
||||
);
|
||||
|
||||
api::respond(200, true, "OK");
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
if (!Polygon::$GamesEnabled) api::respond(200, false, "Games are currently closed. See <a href=\"/forum\">this announcement</a> for more information.");
|
||||
|
||||
$ServerID = api::GetParameter("POST", "ServerID", "int");
|
||||
$Username = api::GetParameter("POST", "Username", "string");
|
||||
$Action = api::GetParameter("POST", "Action", ["Add", "Remove"]);
|
||||
|
||||
$ServerInfo = db::run("SELECT * FROM selfhosted_servers WHERE id = :ServerID", [":ServerID" => $ServerID])->fetch(PDO::FETCH_OBJ);
|
||||
if (!$ServerInfo || !Users::IsAdmin(Users::STAFF_ADMINISTRATOR) && $ServerInfo->hoster != SESSION["user"]["id"]) api::respond(200, false, "You do not have permission to configure this server");
|
||||
if ($ServerInfo->Privacy != "Private") api::respond(200, false, "The privacy of this server must first be set to Private");
|
||||
if ($ServerInfo->LastWhitelistEdit+30 > time()) api::respond(200, false, "Please wait ".GetReadableTime($ServerInfo->LastWhitelistEdit, ["RelativeTime" => "30 seconds"])." before editing your whitelist");
|
||||
|
||||
$Whitelist = ($ServerInfo->PrivacyWhitelist == null) ? [] : json_decode($ServerInfo->PrivacyWhitelist);
|
||||
|
||||
$UserInfo = Users::GetInfoFromName($Username);
|
||||
if (!$UserInfo) api::respond(200, false, "That username is not on Project Polygon");
|
||||
|
||||
if ($Action == "Add")
|
||||
{
|
||||
if ((int) $UserInfo->id == SESSION["user"]["id"]) api::respond(200, false, "You cannot add yourself to the whitelist");
|
||||
if (in_array((int) $UserInfo->id, $Whitelist)) api::respond(200, false, "That user is already on the whitelist");
|
||||
$Whitelist[] = (int) $UserInfo->id;
|
||||
}
|
||||
else if ($Action == "Remove")
|
||||
{
|
||||
if (!in_array((int) $UserInfo->id, $Whitelist)) api::respond(200, false, "That user is not on the whitelist");
|
||||
|
||||
$Location = array_search((int) $UserInfo->id, $Whitelist);
|
||||
if ($Location === false) api::respond(200, false, "An unexpected error occurred");
|
||||
|
||||
unset($Whitelist[$Location]);
|
||||
}
|
||||
|
||||
db::run(
|
||||
"UPDATE selfhosted_servers SET LastWhitelistEdit = UNIX_TIMESTAMP(), PrivacyWhitelist = :Whitelist WHERE id = :ServerID",
|
||||
[":ServerID" => $ServerID, ":Whitelist" => json_encode($Whitelist)]
|
||||
);
|
||||
|
||||
if ($Action == "Add")
|
||||
api::respond(200, true, "$Username has been added to the whitelist");
|
||||
else if ($Action == "Remove")
|
||||
api::respond(200, true, "$Username has been removed from the whitelist");
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
|
||||
Polygon::ImportClass("Games");
|
||||
Polygon::ImportClass("Catalog");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true]);
|
||||
|
||||
$PlaceID = api::GetParameter("POST", "PlaceID", "int", false);
|
||||
|
||||
$GameJobCount = db::run(
|
||||
"SELECT COUNT(*) FROM GameJobs WHERE Status = \"Ready\" AND PlaceID = :PlaceID",
|
||||
[":PlaceID" => $PlaceID]
|
||||
)->fetchColumn();
|
||||
|
||||
$Pagination = Pagination(api::GetParameter("POST", "Page", "int", 1), $GameJobCount, 5);
|
||||
|
||||
$GameJobs = db::run(
|
||||
"SELECT GameJobs.*, assets.MaxPlayers FROM GameJobs
|
||||
INNER JOIN assets ON assets.id = PlaceID
|
||||
WHERE Status = \"Ready\" AND PlaceID = :PlaceID
|
||||
ORDER BY PlayerCount DESC LIMIT 5 OFFSET :Offset",
|
||||
[":PlaceID" => $PlaceID, ":Offset" => $Pagination->Offset]
|
||||
);
|
||||
|
||||
if ($GameJobs->rowCount() == 0)
|
||||
{
|
||||
api::respond(200, true, "No games are currently running for this place");
|
||||
}
|
||||
|
||||
while ($GameJob = $GameJobs->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$Items[] =
|
||||
[
|
||||
"JobID" => $GameJob->JobID,
|
||||
"PlayerCount" => (int) $GameJob->PlayerCount,
|
||||
"MaximumPlayers" => (int) $GameJob->MaxPlayers,
|
||||
"IngamePlayers" => Games::GetPlayersInGame($GameJob->JobID)
|
||||
];
|
||||
}
|
||||
|
||||
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $Pagination->Pages, "items" => $Items]));
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
|
||||
Polygon::ImportClass("Games");
|
||||
Polygon::ImportClass("Catalog");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true]);
|
||||
|
||||
$Filters =
|
||||
[
|
||||
"Default" => "ServerRunning DESC, ActivePlayers DESC, LastServerUpdate DESC, updated DESC",
|
||||
"Top Played" => "Visits DESC",
|
||||
"Recently Updated" => "updated DESC"
|
||||
];
|
||||
|
||||
$Query = api::GetParameter("POST", "Query", "string", "");
|
||||
$FilterBy = api::GetParameter("POST", "FilterBy", ["Default", "Top Played", "Recently Updated"], "Default");
|
||||
$Version = api::GetParameter("POST", "FilterVersion", ["All", "2010", "2011", "2012"], "All");
|
||||
$CreatorID = api::GetParameter("POST", "CreatorID", "int", false);
|
||||
|
||||
$QueryParameters = "type = 9";
|
||||
$ValueParameters = [];
|
||||
|
||||
if (strlen($Query))
|
||||
{
|
||||
$QueryParameters .= " AND name LIKE :Query";
|
||||
$ValueParameters[":Query"] = "%{$Query}%";
|
||||
}
|
||||
|
||||
if ($Version != "All")
|
||||
{
|
||||
$QueryParameters .= " AND Version = :Version";
|
||||
$ValueParameters[":Version"] = $Version;
|
||||
}
|
||||
|
||||
if ($CreatorID !== false)
|
||||
{
|
||||
$Limit = 10;
|
||||
$OrderBy = "created DESC";
|
||||
$QueryParameters .= " AND creator = :CreatorID";
|
||||
$ValueParameters[":CreatorID"] = $CreatorID;
|
||||
}
|
||||
else
|
||||
{
|
||||
$Limit = 24;
|
||||
$OrderBy = $Filters[$FilterBy];
|
||||
|
||||
}
|
||||
|
||||
$PlaceCount = db::run("SELECT COUNT(*) FROM assets WHERE {$QueryParameters}", $ValueParameters)->fetchColumn();
|
||||
|
||||
$Pagination = Pagination(api::GetParameter("POST", "Page", "int", 1), $PlaceCount, $Limit);
|
||||
$ValueParameters[":Limit"] = $Limit;
|
||||
$ValueParameters[":Offset"] = $Pagination->Offset;
|
||||
|
||||
$Places = db::run(
|
||||
"SELECT assets.*, users.username FROM assets
|
||||
INNER JOIN users ON users.id = assets.creator
|
||||
WHERE {$QueryParameters} ORDER BY {$OrderBy} LIMIT :Limit OFFSET :Offset",
|
||||
$ValueParameters
|
||||
);
|
||||
|
||||
if ($Places->rowCount() == 0)
|
||||
{
|
||||
if ($CreatorID === false)
|
||||
{
|
||||
api::respond(200, true, "No games matched your query");
|
||||
}
|
||||
else if ($CreatorID == SESSION["user"]["id"])
|
||||
{
|
||||
api::respond(200, true, "You do not have any active places. <a href=\"/develop?View=9\">Manage My Places</a>");
|
||||
}
|
||||
else
|
||||
{
|
||||
api::respond(200, true, Users::GetNameFromID($CreatorID) . " does not have any active places");
|
||||
}
|
||||
}
|
||||
|
||||
while ($Place = $Places->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$Items[] =
|
||||
[
|
||||
"PlaceID" => (int) $Place->id,
|
||||
"Name" => Polygon::FilterText($Place->name),
|
||||
"Description" => Polygon::FilterText($Place->description),
|
||||
"Visits" => number_format($Place->Visits),
|
||||
"OnlinePlayers" => $Place->ServerRunning ? number_format($Place->ActivePlayers) : false,
|
||||
"Location" => "/" . encode_asset_name($Place->name) . "-place?id={$Place->id}",
|
||||
"Thumbnail" => Thumbnails::GetAsset($Place, 768, 432),
|
||||
"CreatorName" => $Place->username,
|
||||
"CreatorID" => $Place->creator,
|
||||
"Version" => (int) $Place->Version,
|
||||
"Uncopylocked" => (bool) $Place->publicDomain
|
||||
];
|
||||
}
|
||||
|
||||
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $Pagination->Pages, "items" => $Items]));
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
|
||||
Polygon::ImportClass("Games");
|
||||
Polygon::ImportClass("Catalog");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true]);
|
||||
|
||||
$Version = api::GetParameter("POST", "Version", ["Any", "2009", "2010", "2011", "2012"], "Any");
|
||||
$CreatorID = api::GetParameter("POST", "CreatorID", "int", false);
|
||||
|
||||
if (!Polygon::$GamesEnabled) api::respond(200, false, "Games are currently closed. See <a href=\"/forum/showpost?PostID=2380\">this announcement</a> for more information.");
|
||||
|
||||
$query_params = "1 AND (Privacy = \"Public\" OR hoster = :UserID OR JSON_CONTAINS(PrivacyWhitelist, :UserID, \"$\"))";
|
||||
$value_params = [":UserID" => SESSION["user"]["id"]];
|
||||
|
||||
if($Version != "Any")
|
||||
{
|
||||
$query_params .= " AND version = :Version";
|
||||
$value_params[":Version"] = (int) $Version;
|
||||
}
|
||||
|
||||
if($CreatorID !== false)
|
||||
{
|
||||
$query_params .= " AND hoster = :HosterID";
|
||||
$value_params[":HosterID"] = $CreatorID;
|
||||
}
|
||||
|
||||
$ServersCount = db::run("SELECT COUNT(*) FROM selfhosted_servers WHERE $query_params", $value_params)->fetchColumn();
|
||||
|
||||
$Pagination = Pagination(api::GetParameter("POST", "Page", "int", 1), $ServersCount, 10);
|
||||
$value_params[":Offset"] = $Pagination->Offset;
|
||||
|
||||
$Servers = db::run(
|
||||
"SELECT selfhosted_servers.*, users.username,
|
||||
(ping+35 > UNIX_TIMESTAMP()) AS online,
|
||||
(
|
||||
CASE WHEN ping+35 > UNIX_TIMESTAMP() THEN
|
||||
(SELECT COUNT(DISTINCT uid) FROM client_sessions WHERE ping+35 > UNIX_TIMESTAMP() AND serverID = selfhosted_servers.id AND verified AND valid)
|
||||
ELSE 0 END
|
||||
) AS players
|
||||
FROM selfhosted_servers
|
||||
INNER JOIN users ON users.id = selfhosted_servers.hoster
|
||||
WHERE $query_params
|
||||
ORDER BY online DESC, players DESC, ping DESC, created DESC LIMIT 10 OFFSET :Offset",
|
||||
$value_params
|
||||
);
|
||||
|
||||
if ($Servers->rowCount() == 0)
|
||||
{
|
||||
if ($CreatorID === false)
|
||||
{
|
||||
api::respond(200, true, "No servers matched your query");
|
||||
}
|
||||
else
|
||||
{
|
||||
api::respond(200, true, Users::GetNameFromID($CreatorID)." does not have any games");
|
||||
}
|
||||
}
|
||||
|
||||
while ($Server = $Servers->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$Gears = [];
|
||||
foreach (json_decode($Server->allowed_gears, true) as $GearName => $GearEnabled)
|
||||
{
|
||||
if (!$GearEnabled) continue;
|
||||
$Gears[] = ["name" => Catalog::$GearAttributesDisplay[$GearName]["text_sel"], "icon" => Catalog::$GearAttributesDisplay[$GearName]["icon"]];
|
||||
}
|
||||
|
||||
$Items[] =
|
||||
[
|
||||
"server_id" => (int) $Server->id,
|
||||
"server_name" => Polygon::FilterText($Server->name),
|
||||
"server_description" => empty($Server->description) ? "No description available." : Polygon::FilterText($Server->description),
|
||||
"server_thumbnail" => Thumbnails::GetAvatar($Server->hoster),
|
||||
"hoster_name" => $Server->username,
|
||||
"hoster_id" => $Server->hoster,
|
||||
"date" => date('n/d/Y g:i:s A', $Server->created),
|
||||
"version" => (int) $Server->version,
|
||||
"server_online" => (bool) $Server->online,
|
||||
"players_online" => (int) $Server->players,
|
||||
"players_max" => (int) $Server->maxplayers,
|
||||
"privacy" => $Server->Privacy,
|
||||
"gears" => $Gears
|
||||
];
|
||||
}
|
||||
|
||||
db::run("INSERT INTO log (UserID, Timestamp, IPAddress) VALUES (:UserID, UNIX_TIMESTAMP(), :IPAddress)", [":UserID" => SESSION["user"]["id"], ":IPAddress" => GetIPAddress()]);
|
||||
|
||||
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $Pagination->Pages, "items" => $Items]));
|
||||
|
|
@ -0,0 +1,232 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Games");
|
||||
Polygon::ImportClass("Discord");
|
||||
|
||||
header("Pragma: no-cache");
|
||||
header("Cache-Control: no-cache");
|
||||
|
||||
$Statuses =
|
||||
[
|
||||
"Waiting" =>
|
||||
[
|
||||
"Message" => "Waiting for a server",
|
||||
"Code" => 0
|
||||
],
|
||||
|
||||
"Loading" =>
|
||||
[
|
||||
"Message" => "A server is loading the game",
|
||||
"Code" => 1
|
||||
],
|
||||
|
||||
"Joining" =>
|
||||
[
|
||||
"Message" => "The server is ready. Joining the game...",
|
||||
"Code" => 2
|
||||
],
|
||||
|
||||
"Error" =>
|
||||
[
|
||||
"Message" => "An error occured. Please try again later",
|
||||
"Code" => 4
|
||||
],
|
||||
|
||||
"Expired" =>
|
||||
[
|
||||
"Message" => "There are no game servers available at this time. Please try again later.",
|
||||
"Code" => 4
|
||||
],
|
||||
|
||||
"GameEnded" =>
|
||||
[
|
||||
"Message" => "The game you requested has ended",
|
||||
"Code" => 5
|
||||
],
|
||||
|
||||
"GameFull" =>
|
||||
[
|
||||
"Message" => "The game you requested is full. Please try again later",
|
||||
"Code" => 6
|
||||
],
|
||||
|
||||
"Ratelimit" =>
|
||||
[
|
||||
"Message" => "You are joining games too fast. Please try again later",
|
||||
"Code" => 11
|
||||
],
|
||||
|
||||
"Unauthorized" =>
|
||||
[
|
||||
"Message" => "Cannot join game with no authenticated user.",
|
||||
"Code" => 4
|
||||
]
|
||||
];
|
||||
|
||||
$IsTeleport = isset($_GET["isTeleport"]) && $_GET['isTeleport'] == "true";
|
||||
|
||||
if ($IsTeleport)
|
||||
{
|
||||
$UserInfo = Users::GetInfoFromJobTicket();
|
||||
}
|
||||
else
|
||||
{
|
||||
api::initialize(["method" => "GET", "logged_in" => true, "secure" => true]);
|
||||
$UserInfo = (object)SESSION["user"];
|
||||
}
|
||||
|
||||
$Request = api::GetParameter("GET", "request", ["RequestGame", "RequestGameJob", "RequestFollowUser", "CheckGameJobStatus"]);
|
||||
|
||||
if ($IsTeleport && GetUserAgent() != "Roblox/WinInet")
|
||||
{
|
||||
die(json_encode([
|
||||
"Error" => "Request is not authorized from specified origin",
|
||||
"userAgent" => $_SERVER["HTTP_USER_AGENT"] ?? null,
|
||||
"referrer" => $_SERVER["HTTP_REFERER"] ?? null
|
||||
]));
|
||||
}
|
||||
|
||||
if (!$UserInfo)
|
||||
{
|
||||
Respond("Unauthorized");
|
||||
}
|
||||
|
||||
function Respond($Status, $JobID = null, $Version = null, $JoinScriptUrl = null)
|
||||
{
|
||||
global $Statuses;
|
||||
|
||||
$Response = [];
|
||||
$StatusInfo = $Statuses[$Status];
|
||||
|
||||
$Response["jobId"] = $JobID;
|
||||
$Response["status"] = $StatusInfo["Code"];
|
||||
$Response["joinScriptUrl"] = $JoinScriptUrl;
|
||||
$Response["authenticationUrl"] = $JoinScriptUrl ? "https://{$_SERVER['HTTP_HOST']}/Login/Negotiate.ashx" : null;
|
||||
$Response["authenticationTicket"] = $JoinScriptUrl ? "0" : null;
|
||||
$Response["message"] = $StatusInfo["Message"];
|
||||
$Response["version"] = $Version;
|
||||
|
||||
die(json_encode($Response));
|
||||
}
|
||||
|
||||
|
||||
function CreateNewSession($Job)
|
||||
{
|
||||
global $UserInfo, $IsTeleport;
|
||||
|
||||
$Ticket = generateUUID();
|
||||
$SecurityTicket = generateUUID();
|
||||
|
||||
$SessionsRequested = db::run(
|
||||
"SELECT COUNT(*) FROM GameJobSessions WHERE UserID = :UserID AND TimeCreated + 60 > UNIX_TIMESTAMP()",
|
||||
[":UserID" => $UserInfo->id]
|
||||
)->fetchColumn();
|
||||
|
||||
if ($SessionsRequested >= 2) Respond("Ratelimit"); // api::respond(200, false, "You are joining games too fast. Please try again later");
|
||||
|
||||
db::run(
|
||||
"INSERT INTO GameJobSessions (Ticket, SecurityTicket, JobID, IsTeleport, UserID, TimeCreated)
|
||||
VALUES (:Ticket, :SecurityTicket, :JobID, :IsTeleport, :UserID, UNIX_TIMESTAMP())",
|
||||
[":Ticket" => $Ticket, ":SecurityTicket" => $SecurityTicket, ":JobID" => $Job->JobID, ":IsTeleport" => (int)$IsTeleport, ":UserID" => $UserInfo->id]
|
||||
);
|
||||
|
||||
Respond("Joining", $Job->JobID, $Job->Version, "http://{$_SERVER['HTTP_HOST']}/Game/Join.ashx?JobTicket={$Ticket}");
|
||||
}
|
||||
|
||||
if ($Request == "RequestGame") // clicking the "play" button
|
||||
{
|
||||
$PlaceID = api::GetParameter("GET", "placeId", "int");
|
||||
$PlaceInfo = db::run("SELECT * FROM assets WHERE id = :PlaceID", [":PlaceID" => $PlaceID])->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
if (!$PlaceInfo || $PlaceInfo->type != 9) Respond("Error"); //api::respond(200, false, "An error occured. Please try again later");
|
||||
|
||||
// check for an available game job
|
||||
$AvailableJob = db::run(
|
||||
"SELECT GameJobs.* FROM GameJobs
|
||||
WHERE NOT Status IN (\"Closed\", \"Crashed\")
|
||||
AND PlaceID = :PlaceID
|
||||
AND PlayerCount < :MaxPlayers
|
||||
LIMIT 1",
|
||||
[":PlaceID" => $PlaceID, ":MaxPlayers" => $PlaceInfo->MaxPlayers]
|
||||
)->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
if ($AvailableJob)
|
||||
{
|
||||
if ($AvailableJob->Status == "Ready")
|
||||
{
|
||||
CreateNewSession($AvailableJob);
|
||||
}
|
||||
else
|
||||
{
|
||||
Respond($AvailableJob->Status == "Pending" ? "Waiting" : "Loading", $AvailableJob->JobID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// get an available server
|
||||
$GameServer = db::run(
|
||||
"SELECT * FROM GameServers
|
||||
WHERE Online
|
||||
AND LastUpdated + 35 > UNIX_TIMESTAMP()
|
||||
AND ActiveJobs < MaximumJobs
|
||||
AND CpuUsage < 90
|
||||
AND AvailableMemory > 1000
|
||||
ORDER BY Priority ASC LIMIT 1"
|
||||
)->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
if (!$GameServer) Respond("Expired");
|
||||
|
||||
$JobID = generateUUID();
|
||||
|
||||
$ServersRequested = db::run(
|
||||
"SELECT COUNT(*) FROM GameJobs WHERE RequestedBy = :UserID AND TimeCreated + 60 > UNIX_TIMESTAMP()",
|
||||
[":UserID" => $UserInfo->id]
|
||||
)->fetchColumn();
|
||||
|
||||
if ($ServersRequested >= 2) api::respond(200, false, "You are joining games too fast. Please try again later");
|
||||
|
||||
// request a new job
|
||||
$GameJob = db::run(
|
||||
"INSERT INTO GameJobs (RequestedBy, JobID, ServerID, Version, PlaceID, TimeCreated, LastUpdated)
|
||||
VALUES (:UserID, :JobID, :ServerID, :Version, :PlaceID, UNIX_TIMESTAMP(), UNIX_TIMESTAMP())",
|
||||
[
|
||||
":UserID" => $UserInfo->id,
|
||||
":JobID" => $JobID,
|
||||
":ServerID" => $GameServer->ServerID,
|
||||
":Version" => $PlaceInfo->Version,
|
||||
":PlaceID" => $PlaceInfo->id
|
||||
]
|
||||
);
|
||||
|
||||
$Request = "{\"Operation\":\"OpenJob\", \"JobID\":\"{$JobID}\", \"Version\":{$PlaceInfo->Version}, \"PlaceID\":{$PlaceInfo->id}}";
|
||||
$Socket = fsockopen($GameServer->ServiceAddress, $GameServer->ServicePort);
|
||||
fwrite($Socket, $Request);
|
||||
fclose($Socket);
|
||||
|
||||
Respond("Waiting", $JobID);
|
||||
}
|
||||
}
|
||||
else if ($Request == "RequestFollowUser") // joining a user's game
|
||||
{
|
||||
|
||||
}
|
||||
else if ($Request == "RequestGameJob" || $Request == "CheckGameJobStatus")
|
||||
{
|
||||
$JobID = api::GetParameter("GET", "jobId", "string");
|
||||
|
||||
// check for an available game job
|
||||
$AvailableJob = db::run(
|
||||
"SELECT GameJobs.*, assets.MaxPlayers FROM GameJobs
|
||||
INNER JOIN assets ON assets.id = PlaceID
|
||||
WHERE JobID = :JobID",
|
||||
[":JobID" => $JobID]
|
||||
)->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
if (!$AvailableJob) Respond("Error");
|
||||
if ($AvailableJob->Status == "Closed" || $AvailableJob->Status == "Crashed") Respond("GameEnded");
|
||||
if ($AvailableJob->PlayerCount >= $AvailableJob->MaxPlayers) Respond("GameFull");
|
||||
|
||||
if ($AvailableJob->Status == "Pending") Respond("Waiting", $JobID);
|
||||
if ($AvailableJob->Status == "Loading") Respond("Loading", $JobID);
|
||||
|
||||
CreateNewSession($AvailableJob);
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
|
||||
header("Pragma: no-cache");
|
||||
header("Cache-Control: no-cache");
|
||||
|
||||
Polygon::ImportClass("Games");
|
||||
Polygon::ImportClass("Discord");
|
||||
|
||||
api::initialize(["method" => "GET", "logged_in" => true, "secure" => true]);
|
||||
|
||||
if (!Polygon::$GamesEnabled) api::respond(200, false, "Games are currently closed. See <a href=\"/forum\">this announcement</a> for more information.");
|
||||
|
||||
$serverID = $_GET["serverID"] ?? $_GET['placeId'] ?? false;
|
||||
$isTeleport = isset($_GET["isTeleport"]) && $_GET['isTeleport'] == "true";
|
||||
|
||||
if($isTeleport && GetUserAgent() != "Roblox/WinInet")
|
||||
api::respond_custom([
|
||||
"Error" => "Request is not authorized from specified origin",
|
||||
"userAgent" => $_SERVER["HTTP_USER_AGENT"] ?? null,
|
||||
"referrer" => $_SERVER["HTTP_REFERER"] ?? null
|
||||
]);
|
||||
|
||||
/* if($isTeleport)
|
||||
{
|
||||
$ticket = $_COOKIE['ticket'] ?? false;
|
||||
$query = $pdo->prepare("SELECT uid FROM client_sessions WHERE ticket = :ticket");
|
||||
$query->bindParam(":ticket", $ticket, PDO::PARAM_STR);
|
||||
$query->execute();
|
||||
if(!$query->rowCount()) api::respond_custom(["Error" => "You are not logged in"]);
|
||||
$userid = $query->fetchColumn();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!SESSION) api::respond(200, false, "You are not logged in");
|
||||
$userid = SESSION["user"]["id"];
|
||||
} */
|
||||
|
||||
// if (!Discord::IsVerified(SESSION["user"]["id"]))
|
||||
// api::respond(200, false, "You must verify yourself in the <a href=\"https://discord.com/invite/projectpolygon\">Discord server</a> before joining a game.");
|
||||
|
||||
/* $query = $pdo->prepare("SELECT *, (SELECT COUNT(*) FROM client_sessions WHERE ping+35 > UNIX_TIMESTAMP() AND serverID = selfhosted_servers.id AND valid) AS players FROM selfhosted_servers WHERE id = :sid");
|
||||
$query->bindParam(":sid", $serverID, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
$serverInfo = $query->fetch(PDO::FETCH_OBJ); */
|
||||
|
||||
$serverInfo = Games::GetServerInfo($serverID, SESSION["user"]["id"], true);
|
||||
|
||||
if(!$serverInfo) api::respond(200, false, "Server does not exist");
|
||||
if(!$serverInfo->online) api::respond(200, false, "This server is currently offline.");
|
||||
if($serverInfo->players >= $serverInfo->maxplayers) api::respond(200, false, "This server is currently full. Please try again later");
|
||||
if($serverInfo->version == 2009 && SESSION["user"]["id"] > 200) api::respond(200, false, "2009 games are currently disabled.");
|
||||
|
||||
$ticket = generateUUID();
|
||||
$securityTicket = generateUUID();
|
||||
db::run(
|
||||
"INSERT INTO client_sessions (ticket, securityTicket, uid, sessionType, serverID, created, isTeleport, RequesterIP)
|
||||
VALUES (:uuid, :security, :uid, 1, :sid, UNIX_TIMESTAMP(), :teleport, :ip)",
|
||||
[":uuid" => $ticket, ":security" => $securityTicket, ":uid" => SESSION["user"]["id"], ":sid" => $serverID, ":teleport" => (int)$isTeleport, ":ip" => GetIPAddress()]
|
||||
);
|
||||
|
||||
$Protocol = "https";
|
||||
if($serverInfo->version == 2009) $Protocol = "http";
|
||||
|
||||
api::respond_custom([
|
||||
"status" => 200,
|
||||
"success" => true,
|
||||
"message" => "OK",
|
||||
"version" => $serverInfo->version,
|
||||
"joinScriptUrl" => "{$Protocol}://{$_SERVER['HTTP_HOST']}/game/join?ticket={$ticket}",
|
||||
// these last few params are for teleportservice and lack any function - just ignore
|
||||
"authenticationUrl" => "{$Protocol}://{$_SERVER['HTTP_HOST']}/Login/Negotiate.ashx",
|
||||
"authenticationTicket" => "0",
|
||||
"status" => 2
|
||||
]);
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Games");
|
||||
Polygon::ImportClass("Discord");
|
||||
|
||||
api::initialize(["method" => "GET", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$JobID = api::GetParameter("GET", "jobId", "string");
|
||||
$JobInfo = db::run("SELECT * FROM GameJobs WHERE JobID = :JobID", [":JobID" => $JobID])->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
if (!$JobInfo) api::respond(200, false, "The requested game does not exist");
|
||||
|
||||
$ServerInfo = db::run("SELECT * FROM GameServers WHERE ServerID = :ServerID", [":ServerID" => $JobInfo->ServerID])->fetch(PDO::FETCH_OBJ);
|
||||
$PlaceInfo = db::run("SELECT * FROM assets WHERE id = :PlaceID", [":PlaceID" => $JobInfo->PlaceID])->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
if ($PlaceInfo->creator != SESSION["user"]["id"] && !Users::IsAdmin(Users::STAFF_ADMINISTRATOR)) api::respond(200, false, "The requested game cannot be shut down");
|
||||
if ($JobInfo->Status == "Closed" || $JobInfo->Status == "Crashed") api::respond(200, false, "The requested game has already been shut down");
|
||||
if ($JobInfo->Status != "Ready") api::respond(200, false, "The requested game cannot be shut down");
|
||||
|
||||
$Request = "{\"Operation\":\"CloseJob\", \"JobID\":\"{$JobID}\"}";
|
||||
$Socket = fsockopen($ServerInfo->ServiceAddress, $ServerInfo->ServicePort);
|
||||
fwrite($Socket, $Request);
|
||||
fclose($Socket);
|
||||
|
||||
api::respond(200, true, "The requested game has been shut down");
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Groups");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
|
||||
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
|
||||
|
||||
if(isset($_POST["Page"]) && !is_numeric($_POST["Page"])) api::respond(400, false, "Page is not a number");
|
||||
|
||||
$GroupID = $_POST["GroupID"] ?? false;
|
||||
$Page = $_POST["Page"] ?? 1;
|
||||
$Members = [];
|
||||
|
||||
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
|
||||
$Rank = Groups::GetUserRank(SESSION["user"]["id"], $GroupID);
|
||||
|
||||
if($Rank->Level == 0) api::respond(200, false, "You are not a member of this group");
|
||||
if(!$Rank->Permissions->CanManageGroupAdmin) api::respond(200, false, "You are not allowed to perform this action");
|
||||
|
||||
$MemberCount = db::run(
|
||||
"SELECT COUNT(*) FROM groups_members
|
||||
WHERE GroupID = :GroupID AND Rank < :RankLevel AND NOT Pending",
|
||||
[":GroupID" => $GroupID, ":RankLevel" => $Rank->Level]
|
||||
)->fetchColumn();
|
||||
|
||||
$Pages = ceil($MemberCount/12);
|
||||
$Offset = ($Page - 1)*12;
|
||||
|
||||
if(!$Pages) api::respond(200, true, "This group does not have any members.");
|
||||
|
||||
$MembersQuery = db::run(
|
||||
"SELECT users.username, users.id, Rank FROM groups_members
|
||||
INNER JOIN users ON users.id = groups_members.UserID
|
||||
WHERE GroupID = :GroupID AND Rank < :RankLevel AND NOT Pending
|
||||
ORDER BY Joined DESC LIMIT 12 OFFSET $Offset",
|
||||
[":GroupID" => $GroupID, ":RankLevel" => $Rank->Level]
|
||||
);
|
||||
|
||||
while($Member = $MembersQuery->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$Members[] =
|
||||
[
|
||||
"UserName" => $Member->username,
|
||||
"UserID" => $Member->id,
|
||||
"RoleLevel" => $Member->Rank,
|
||||
"Avatar" => Thumbnails::GetAvatar($Member->id)
|
||||
];
|
||||
}
|
||||
|
||||
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $Pages, "count" => $MemberCount, "items" => $Members]));
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Groups");
|
||||
|
||||
api::initialize(["method" => "POST"]);
|
||||
|
||||
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
|
||||
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
|
||||
|
||||
$GroupID = $_POST["GroupID"] ?? false;
|
||||
$Roles = [];
|
||||
|
||||
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
|
||||
$Rank = Groups::GetUserRank(SESSION["user"]["id"], $GroupID);
|
||||
|
||||
if($Rank->Level == 0) api::respond(200, false, "You are not a member of this group");
|
||||
if(!$Rank->Permissions->CanManageGroupAdmin) api::respond(200, false, "You are not allowed to perform this action");
|
||||
|
||||
if($Rank->Level == 255)
|
||||
{
|
||||
$RolesQuery = db::run(
|
||||
"SELECT * FROM groups_ranks WHERE GroupID = :GroupID ORDER BY Rank ASC",
|
||||
[":GroupID" => $GroupID]
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$RolesQuery = db::run(
|
||||
"SELECT * FROM groups_ranks WHERE GroupID = :GroupID AND Rank < :MyRank ORDER BY Rank ASC",
|
||||
[":GroupID" => $GroupID, ":MyRank" => $Rank->Level]
|
||||
);
|
||||
}
|
||||
|
||||
while($Role = $RolesQuery->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$Roles[] =
|
||||
[
|
||||
"Name" => htmlspecialchars($Role->Name),
|
||||
"Description" => htmlspecialchars($Role->Description),
|
||||
"Rank" => $Role->Rank,
|
||||
"Permissions" => json_decode($Role->Permissions)
|
||||
];
|
||||
}
|
||||
|
||||
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "items" => $Roles]));
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Groups");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
|
||||
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
|
||||
|
||||
if(!isset($_POST["Recipient"])) api::respond(400, false, "Recipient is not set");
|
||||
|
||||
if(!isset($_POST["Type"])) api::respond(400, false, "Type is not set");
|
||||
if(!in_array($_POST["Type"], ["ally", "enemy"])) api::respond(400, false, "Type is not valid");
|
||||
|
||||
$GroupID = $_POST["GroupID"] ?? false;
|
||||
$RecipientName = $_POST["Recipient"] ?? false;
|
||||
$Type = $_POST["Type"] ?? false;
|
||||
$Groups = [];
|
||||
|
||||
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
|
||||
|
||||
$Recipient = db::run("SELECT * FROM groups WHERE name = :GroupName", [":GroupName" => $RecipientName]);
|
||||
$RecipientInfo = $Recipient->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
if(!$Recipient->rowCount()) api::respond(200, false, "No group with that name exists");
|
||||
|
||||
$MyRank = Groups::GetUserRank(SESSION["user"]["id"], $GroupID);
|
||||
if(!$MyRank->Permissions->CanManageRelationships) api::respond(200, false, "You are not allowed to manage this group's relationships");
|
||||
|
||||
if($RecipientInfo->id == $GroupID)
|
||||
{
|
||||
if($Type == "ally") api::respond(200, false, "You cannot send an ally request to your own group");
|
||||
else if($Type == "enemy") api::respond(200, false, "You cannot declare your own group as an enemy");
|
||||
}
|
||||
|
||||
$Relationship = db::run(
|
||||
"SELECT * FROM groups_relationships WHERE :GroupID IN (Declarer, Recipient) AND :Recipient IN (Declarer, Recipient) AND Status != 2",
|
||||
[":GroupID" => $GroupID, ":Recipient" => $RecipientInfo->id]
|
||||
);
|
||||
$RelationshipInfo = $Relationship->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
if($Relationship->rowCount())
|
||||
{
|
||||
if($RelationshipInfo->Type == "Allies")
|
||||
{
|
||||
if($RelationshipInfo->Status == 0)
|
||||
{
|
||||
if($RelationshipInfo->Declarer == $GroupID)
|
||||
{
|
||||
api::respond(200, false, "You already have an outgoing ally request to this group");
|
||||
}
|
||||
else
|
||||
{
|
||||
api::respond(200, false, "You already have an incoming ally request from this group");
|
||||
}
|
||||
}
|
||||
else if($RelationshipInfo->Status == 1)
|
||||
{
|
||||
api::respond(200, false, "You are already allies with this group!");
|
||||
}
|
||||
}
|
||||
else if($RelationshipInfo->Type == "Enemies")
|
||||
{
|
||||
api::respond(200, false, "You are already enemies with this group!");
|
||||
}
|
||||
}
|
||||
|
||||
if($Type == "ally")
|
||||
{
|
||||
$LastRequest = db::run("SELECT Declared FROM groups_relationships WHERE Declarer = :GroupID AND Declared+3600 > UNIX_TIMESTAMP()", [":GroupID" => $GroupID]);
|
||||
if($LastRequest->rowCount())
|
||||
api::respond(429, false, "Please wait ".GetReadableTime($LastRequest->fetchColumn(), ["RelativeTime" => "1 hour"])." before sending a new ally request");
|
||||
|
||||
db::run(
|
||||
"INSERT INTO groups_relationships (Type, Declarer, Recipient, Status, Declared)
|
||||
VALUES (\"Allies\", :GroupID, :Recipient, 0, UNIX_TIMESTAMP())",
|
||||
[":GroupID" => $GroupID, ":Recipient" => $RecipientInfo->id]
|
||||
);
|
||||
|
||||
Groups::LogAction(
|
||||
$GroupID, "Send Ally Request",
|
||||
sprintf(
|
||||
"<a href=\"/user?ID=%d\">%s</a> sent an ally request to <a href=\"/groups?gid=%d\">%s</a>",
|
||||
SESSION["user"]["id"], SESSION["user"]["username"], $RecipientInfo->id, htmlspecialchars($RecipientInfo->name)
|
||||
)
|
||||
);
|
||||
api::respond(200, true, "Ally request has been sent to ".Polygon::FilterText($RecipientInfo->name));
|
||||
}
|
||||
else if($Type == "enemy")
|
||||
{
|
||||
$LastRequest = db::run("SELECT Declared FROM groups_relationships WHERE Declarer = :GroupID AND Declared+3600 > UNIX_TIMESTAMP()", [":GroupID" => $GroupID]);
|
||||
if($LastRequest->rowCount())
|
||||
api::respond(429, false, "Please wait ".GetReadableTime($LastRequest->fetchColumn(), ["RelativeTime" => "1 hour"])." before sending a new ally request");
|
||||
|
||||
db::run(
|
||||
"INSERT INTO groups_relationships (Type, Declarer, Recipient, Status, Declared, Established)
|
||||
VALUES (\"Enemies\", :GroupID, :Recipient, 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP())",
|
||||
[":GroupID" => $GroupID, ":Recipient" => $RecipientInfo->id]
|
||||
);
|
||||
|
||||
Groups::LogAction(
|
||||
$GroupID, "Create Enemy",
|
||||
sprintf(
|
||||
"<a href=\"/user?ID=%d\">%s</a> declared <a href=\"/groups?gid=%d\">%s</a> as an enemy",
|
||||
SESSION["user"]["id"], SESSION["user"]["username"], $RecipientInfo->id, htmlspecialchars($RecipientInfo->name)
|
||||
)
|
||||
);
|
||||
api::respond(200, true, Polygon::FilterText($RecipientInfo->name)." is now your enemy!");
|
||||
}
|
||||
|
||||
api::respond(200, false, "An unexpected error occurred");
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Groups");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
|
||||
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
|
||||
|
||||
if(!isset($_POST["UserID"])) api::respond(400, false, "GroupID is not set");
|
||||
if(!is_numeric($_POST["UserID"])) api::respond(400, false, "GroupID is not a number");
|
||||
|
||||
if(isset($_POST["RoleLevel"]) && !is_numeric($_POST["RoleLevel"])) api::respond(400, false, "RoleLevel is not a number");
|
||||
|
||||
$GroupID = $_POST["GroupID"] ?? false;
|
||||
$UserID = $_POST["UserID"] ?? false;
|
||||
|
||||
$RoleLevel = $_POST["RoleLevel"] ?? false;
|
||||
|
||||
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
|
||||
|
||||
$MyRole = Groups::GetUserRank(SESSION["user"]["id"], $GroupID);
|
||||
$UserRole = Groups::GetUserRank($UserID, $GroupID);
|
||||
|
||||
if($MyRole->Level == 0) api::respond(200, false, "You are not a member of this group");
|
||||
if(!$MyRole->Permissions->CanManageGroupAdmin) api::respond(200, false, "You are not allowed to perform this action");
|
||||
if($UserRole->Level == 0) api::respond(200, false, "That user is not a member of this group");
|
||||
|
||||
if($RoleLevel !== false)
|
||||
{
|
||||
if(!Groups::GetRankInfo($GroupID, $RoleLevel)) api::respond(200, false, "That role does not exist");
|
||||
if($RoleLevel == 0 || $RoleLevel == 255) api::respond(200, false, "That role cannot be manually assigned to a member");
|
||||
|
||||
if($UserRole->Level == $RoleLevel) api::respond(200, false, "The role you tried to assign is the user's current role");
|
||||
if($MyRole->Level <= $RoleLevel) api::respond(200, false, "You can only assign roles lower than yours");
|
||||
if($MyRole->Level <= $UserRole->Level) api::respond(200, false, "You can only modify the role of a user who is a role lower than yours");
|
||||
|
||||
db::run(
|
||||
"UPDATE groups_members SET Rank = :RoleLevel WHERE GroupID = :GroupID AND UserID = :UserID",
|
||||
[":GroupID" => $GroupID, ":UserID" => $UserID, ":RoleLevel" => $RoleLevel]
|
||||
);
|
||||
|
||||
$UserName = Users::GetNameFromID($UserID);
|
||||
$RoleName = Groups::GetRankInfo($GroupID, $RoleLevel)->Name;
|
||||
$Action = $RoleLevel > $UserRole->Level ? "promoted" : "demoted";
|
||||
|
||||
Groups::LogAction(
|
||||
$GroupID, "Change Rank",
|
||||
sprintf(
|
||||
"<a href=\"/user?ID=%d\">%s</a> %s <a href=\"/user?ID=%d\">%s</a> from %s to %s",
|
||||
SESSION["user"]["id"], SESSION["user"]["username"], $Action, $UserID, $UserName, htmlspecialchars($UserRole->Name), htmlspecialchars($RoleName)
|
||||
)
|
||||
);
|
||||
|
||||
api::respond(200, true, "$UserName has been $Action to " . htmlspecialchars($RoleName));
|
||||
}
|
||||
|
||||
api::respond(200, true, "OK");
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Groups");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
|
||||
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
|
||||
|
||||
if(!isset($_POST["Recipient"])) api::respond(400, false, "Recipient is not set");
|
||||
if(!is_numeric($_POST["Recipient"])) api::respond(400, false, "Recipient is not a number");
|
||||
|
||||
if(!isset($_POST["Action"])) api::respond(400, false, "Action is not set");
|
||||
if(!in_array($_POST["Action"], ["accept", "decline"])) api::respond(400, false, "Action is not valid");
|
||||
|
||||
$GroupID = $_POST["GroupID"] ?? false;
|
||||
$Recipient = $_POST["Recipient"] ?? false;
|
||||
$Action = $_POST["Action"] ?? false;
|
||||
$Groups = [];
|
||||
|
||||
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
|
||||
if(!Groups::GetGroupInfo($Recipient)) api::respond(200, false, "Recipient group does not exist");
|
||||
|
||||
$MyRank = Groups::GetUserRank(SESSION["user"]["id"], $GroupID);
|
||||
if(!$MyRank->Permissions->CanManageRelationships) api::respond(200, false, "You are not allowed to manage this group's relationships");
|
||||
|
||||
$Relationship = db::run(
|
||||
"SELECT groups_relationships.*, groups.name FROM groups_relationships
|
||||
INNER JOIN groups ON groups.id = (CASE WHEN Declarer = :GroupID THEN Recipient ELSE Declarer END)
|
||||
WHERE :GroupID IN (Declarer, Recipient) AND :Recipient IN (Declarer, Recipient) AND Status != 2",
|
||||
[":GroupID" => $GroupID, ":Recipient" => $Recipient]
|
||||
);
|
||||
$RelationshipInfo = $Relationship->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
if(!$Relationship->rowCount()) api::respond(200, false, "You are not in a relationship with this group");
|
||||
|
||||
if($Action == "accept")
|
||||
{
|
||||
if($RelationshipInfo->Type == "Enemies") api::respond(200, false, "You cannot accept an enemy relationship");
|
||||
if($RelationshipInfo->Status != 0) api::respond(200, false, "You are already in a relationship with this group");
|
||||
|
||||
db::run(
|
||||
"UPDATE groups_relationships SET Status = 1, Established = UNIX_TIMESTAMP() WHERE ID = :RelationshipID",
|
||||
[":RelationshipID" => $RelationshipInfo->ID]
|
||||
);
|
||||
|
||||
Groups::LogAction(
|
||||
$GroupID, "Accept Ally Request",
|
||||
sprintf(
|
||||
"<a href=\"/user?ID=%d\">%s</a> accepted an ally request from <a href=\"/groups?gid=%d\">%s</a>",
|
||||
SESSION["user"]["id"], SESSION["user"]["username"], $Recipient, htmlspecialchars($RelationshipInfo->name)
|
||||
)
|
||||
);
|
||||
|
||||
api::respond(200, true, "You have accepted {$RelationshipInfo->name}'s ally request");
|
||||
}
|
||||
else if($Action == "decline")
|
||||
{
|
||||
db::run(
|
||||
"UPDATE groups_relationships SET Status = 2, Broken = UNIX_TIMESTAMP() WHERE ID = :RelationshipID",
|
||||
[":RelationshipID" => $RelationshipInfo->ID]
|
||||
);
|
||||
|
||||
if($RelationshipInfo->Type == "Allies")
|
||||
{
|
||||
if($RelationshipInfo->Status == 0)
|
||||
{
|
||||
Groups::LogAction(
|
||||
$GroupID, "Decline Ally Request",
|
||||
sprintf(
|
||||
"<a href=\"/user?ID=%d\">%s</a> declined an ally request from <a href=\"/groups?gid=%d\">%s</a>",
|
||||
SESSION["user"]["id"], SESSION["user"]["username"], $Recipient, htmlspecialchars($RelationshipInfo->name)
|
||||
)
|
||||
);
|
||||
|
||||
api::respond(200, true, "You have declined ".Polygon::FilterText($RelationshipInfo->name)."'s ally request");
|
||||
}
|
||||
else if($RelationshipInfo->Status == 1)
|
||||
{
|
||||
Groups::LogAction(
|
||||
$GroupID, "Delete Ally",
|
||||
sprintf(
|
||||
"<a href=\"/user?ID=%d\">%s</a> removed <a href=\"/groups?gid=%d\">%s</a> as an ally",
|
||||
SESSION["user"]["id"], SESSION["user"]["username"], $Recipient, htmlspecialchars($RelationshipInfo->name)
|
||||
)
|
||||
);
|
||||
|
||||
api::respond(200, true, "You are no longer allies with ".Polygon::FilterText($RelationshipInfo->name));
|
||||
}
|
||||
}
|
||||
else if($RelationshipInfo->Type == "Enemies")
|
||||
{
|
||||
Groups::LogAction(
|
||||
$GroupID, "Delete Enemy",
|
||||
sprintf(
|
||||
"<a href=\"/user?ID=%d\">%s</a> removed <a href=\"/groups?gid=%d\">%s</a> as an enemy",
|
||||
SESSION["user"]["id"], SESSION["user"]["username"], $Recipient, htmlspecialchars($RelationshipInfo->name)
|
||||
)
|
||||
);
|
||||
|
||||
api::respond(200, true, "You are no longer enemies with ".Polygon::FilterText($RelationshipInfo->name));
|
||||
}
|
||||
}
|
||||
|
||||
api::respond(200, false, "An unexpected error occurred");
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Groups");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
|
||||
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
|
||||
|
||||
if(!isset($_POST["Roles"])) api::respond(400, false, "Roles is not set");
|
||||
|
||||
$GroupID = $_POST["GroupID"];
|
||||
$Roles = json_decode($_POST["Roles"]);
|
||||
|
||||
if(!$Roles) api::respond(400, false, "Roles is not valid JSON");
|
||||
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
|
||||
|
||||
$MyRole = Groups::GetUserRank(SESSION["user"]["id"], $GroupID);
|
||||
|
||||
if($MyRole->Level == 0) api::respond(200, false, "You are not a member of this group");
|
||||
if($MyRole->Level != 255) api::respond(200, false, "You are not allowed to perform this action");
|
||||
|
||||
function FindRolesWithRank($Rank)
|
||||
{
|
||||
global $Roles;
|
||||
$Count = 0;
|
||||
|
||||
foreach ($Roles as $Role)
|
||||
{
|
||||
if (!isset($Role->Rank)) continue;
|
||||
if ($Role->Rank == $Rank) $Count += 1;
|
||||
}
|
||||
|
||||
return $Count;
|
||||
}
|
||||
|
||||
function FindRoleWithRank($Rank)
|
||||
{
|
||||
global $Roles;
|
||||
|
||||
foreach ($Roles as $Role)
|
||||
{
|
||||
if (!isset($Role->Rank)) continue;
|
||||
if ($Role->Rank == $Rank) return $Role;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$Permissions =
|
||||
[
|
||||
"CanViewGroupWall",
|
||||
"CanViewGroupStatus",
|
||||
"CanPostOnGroupWall",
|
||||
"CanPostGroupStatus",
|
||||
"CanDeleteGroupWallPosts",
|
||||
"CanAcceptJoinRequests",
|
||||
"CanKickLowerRankedMembers",
|
||||
"CanRoleLowerRankedMembers",
|
||||
"CanManageRelationships",
|
||||
"CanCreateAssets",
|
||||
"CanConfigureAssets",
|
||||
"CanSpendFunds",
|
||||
"CanManageGames",
|
||||
"CanManageGroupAdmin",
|
||||
"CanViewAuditLog"
|
||||
];
|
||||
|
||||
if(FindRolesWithRank(0) == 0) api::respond(200, false, "You can not remove the Guest role");
|
||||
if(FindRolesWithRank(255) == 0) api::respond(200, false, "You can not remove the Owner role");
|
||||
if(count($Roles) < 3) api::respond(200, false, "There must be at least three roles");
|
||||
if(count($Roles) > 10) api::respond(200, false, "There must be no more than ten roles");
|
||||
|
||||
foreach($Roles as $Role)
|
||||
{
|
||||
if(!isset($Role->Name) || !isset($Role->Description) || !isset($Role->Rank) || !isset($Role->Permissions))
|
||||
api::respond(200, false, "Roles are missing parameters");
|
||||
|
||||
if($Role->Rank < 0 || $Role->Rank > 255) api::respond(200, false, "Each role must have a rank number between 0 and 255");
|
||||
if(FindRolesWithRank($Role->Rank) > 1) api::respond(200, false, "Each role must have a unique rank number");
|
||||
|
||||
$CurrentRole = Groups::GetRankInfo($GroupID, $Role->Rank);
|
||||
|
||||
if($CurrentRole === false) $Role->Action = "Create";
|
||||
else $Role->Action = "Update";
|
||||
|
||||
if($Role->Rank == 0)
|
||||
{
|
||||
if($Role->Name != $CurrentRole->Name || $Role->Description != $CurrentRole->Description)
|
||||
api::respond(200, false, "You can not modify the Guest role");
|
||||
}
|
||||
|
||||
if($Role->Rank == 255 && $Role->Permissions != $CurrentRole->Permissions)
|
||||
api::respond(200, false, "You can not modify the permissions of the Owner role");
|
||||
|
||||
if(strlen($Role->Name) < 3) api::respond(200, false, "Role names must be at least 3 characters long");
|
||||
if(strlen($Role->Name) > 15) api::respond(200, false, "Role names must be no longer than 15 characters");
|
||||
|
||||
if(strlen($Role->Description) < 3) api::respond(200, false, "Role description must at least 3 characters long");
|
||||
if(strlen($Role->Description) > 64) api::respond(200, false, "Role description must be no longer than 64 characters");
|
||||
|
||||
foreach ($Permissions as $Permission)
|
||||
{
|
||||
if(!isset($Role->Permissions->$Permission)) api::respond(200, false, "Role is missing a permission");
|
||||
if(!is_bool($Role->Permissions->$Permission)) api::respond(200, false, "Role permission property must have a boolean value");
|
||||
}
|
||||
|
||||
if(count((array)$Role->Permissions) != count($Permissions)) api::respond(200, false, "Role permissions contains an incorrect number of permissions");
|
||||
}
|
||||
|
||||
foreach($Roles as $Role)
|
||||
{
|
||||
if($Role->Action == "Create")
|
||||
{
|
||||
// if(SESSION["user"]["id"] == 1) echo "Creating Role {$Role->Rank}\r\n";
|
||||
|
||||
db::run(
|
||||
"INSERT INTO groups_ranks (GroupID, Name, Description, Rank, Permissions, Created)
|
||||
VALUES (:GroupID, :Name, :Description, :Rank, :Permissions, UNIX_TIMESTAMP())",
|
||||
[":GroupID" => $GroupID, ":Name" => $Role->Name, ":Description" => $Role->Description, ":Rank" => $Role->Rank, ":Permissions" => json_encode($Role->Permissions)]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$GroupRoles = Groups::GetGroupRanks($GroupID, true);
|
||||
while($ExistingRole = $GroupRoles->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$Role = FindRoleWithRank($ExistingRole->Rank);
|
||||
|
||||
if($Role == false)
|
||||
{
|
||||
// if(SESSION["user"]["id"] == 1) echo "Deleting Role {$ExistingRole->Rank}\r\n";
|
||||
|
||||
// for this one we gotta move the members with a role thats being deleted to the lowest rank
|
||||
// slight issue with this is for a brief period, members assigned the role thats being deleted
|
||||
// will have no role - if the timing is just right this could mess up the view of the group page
|
||||
|
||||
// delete the rank by the oldest id - so that we dont accidentally delete the new one
|
||||
db::run(
|
||||
"DELETE FROM groups_ranks WHERE GroupID = :GroupID AND Rank = :Rank ORDER BY id ASC LIMIT 1",
|
||||
[":GroupID" => $GroupID, ":Rank" => $ExistingRole->Rank]
|
||||
);
|
||||
|
||||
$NewRank = db::run(
|
||||
"SELECT Rank FROM polygon.groups_ranks WHERE GroupID = :GroupID AND Rank != 0 ORDER BY Rank ASC LIMIT 1",
|
||||
[":GroupID" => $GroupID]
|
||||
)->fetchColumn();
|
||||
|
||||
// if(SESSION["user"]["id"] == 1) echo "Updating existing members to {$NewRank}\r\n";
|
||||
|
||||
db::run(
|
||||
"UPDATE groups_members SET Rank = :NewRank WHERE GroupID = :GroupID AND Rank = :Rank",
|
||||
[":GroupID" => $GroupID, ":Rank" => $ExistingRole->Rank, ":NewRank" => $NewRank]
|
||||
);
|
||||
}
|
||||
else if(isset($Role->Action) && $Role->Action == "Update")
|
||||
{
|
||||
// if(SESSION["user"]["id"] == 1) echo "Updating Role {$Role->Rank}\r\n";
|
||||
|
||||
db::run(
|
||||
"UPDATE groups_ranks SET Name = :Name, Description = :Description, Permissions = :Permissions
|
||||
WHERE GroupID = :GroupID AND Rank = :Rank",
|
||||
[":GroupID" => $GroupID, ":Name" => $Role->Name, ":Description" => $Role->Description, ":Rank" => $Role->Rank, ":Permissions" => json_encode($Role->Permissions)]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
die(json_encode(["status" => 200, "success" => true, "message" => "Group roles have successfully been updated"]));
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Groups");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
|
||||
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
|
||||
|
||||
if(!isset($_POST["PostID"])) api::respond(400, false, "PostID is not set");
|
||||
if(!is_numeric($_POST["PostID"])) api::respond(400, false, "PostID is not a number");
|
||||
|
||||
$GroupID = $_POST["GroupID"] ?? false;
|
||||
$PostID = $_POST["PostID"] ?? false;
|
||||
|
||||
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
|
||||
|
||||
$Rank = Groups::GetUserRank(SESSION["user"]["id"], $GroupID);
|
||||
if(!$Rank->Permissions->CanDeleteGroupWallPosts) api::respond(200, false, "You are not allowed to delete wall posts on this group");
|
||||
|
||||
$PostInfo = db::run(
|
||||
"SELECT * FROM groups_wall WHERE id = :PostID AND :GroupID = :GroupID",
|
||||
[":PostID" => $PostID, ":GroupID" => $GroupID]
|
||||
)->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
if(!$PostInfo) api::respond(200, false, "Wall post does not exist");
|
||||
|
||||
Groups::LogAction(
|
||||
$GroupID, "Delete Post",
|
||||
sprintf(
|
||||
"<a href=\"/user?ID=%d\">%s</a> deleted post \"%s\" by <a href=\"/user?ID=%d\">%s</a>",
|
||||
SESSION["user"]["id"], SESSION["user"]["username"], htmlspecialchars($PostInfo->Content), $PostInfo->PosterID, Users::GetNameFromID($PostInfo->PosterID)
|
||||
)
|
||||
);
|
||||
|
||||
db::run(
|
||||
"DELETE FROM groups_wall WHERE id = :PostID AND :GroupID = :GroupID",
|
||||
[":PostID" => $PostID, ":GroupID" => $GroupID]
|
||||
);
|
||||
|
||||
api::respond(200, true, "OK");
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Groups");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST"]);
|
||||
|
||||
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
|
||||
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
|
||||
|
||||
if(isset($_POST["Page"]) && !is_numeric($_POST["Page"])) api::respond(400, false, "Page is not a number");
|
||||
|
||||
$GroupID = $_POST["GroupID"] ?? false;
|
||||
$Filter = $_POST["Filter"] ?? "All Actions";
|
||||
$Page = $_POST["Page"] ?? 1;
|
||||
$Logs = [];
|
||||
|
||||
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
|
||||
$MyRank = Groups::GetUserRank(SESSION["user"]["id"] ?? 0, $GroupID);
|
||||
if(!$MyRank->Permissions->CanViewAuditLog) api::respond(200, false, "You cannot audit this group");
|
||||
|
||||
if($Filter == "All Actions")
|
||||
{
|
||||
$LogCount = db::run(
|
||||
"SELECT COUNT(*) FROM groups_audit WHERE GroupID = :GroupID",
|
||||
[":GroupID" => $GroupID]
|
||||
)->fetchColumn();
|
||||
}
|
||||
else
|
||||
{
|
||||
$LogCount = db::run(
|
||||
"SELECT COUNT(*) FROM groups_audit WHERE GroupID = :GroupID AND Category = :Action",
|
||||
[":GroupID" => $GroupID, ":Action" => $Filter]
|
||||
)->fetchColumn();
|
||||
}
|
||||
|
||||
$Pages = ceil($LogCount/20);
|
||||
$Offset = ($Page - 1)*20;
|
||||
|
||||
if(!$Pages) api::respond(200, true, "This group does not have any logs for this action.");
|
||||
|
||||
if($Filter == "All Actions")
|
||||
{
|
||||
$LogsQuery = db::run(
|
||||
"SELECT groups_audit.*, users.username FROM groups_audit
|
||||
INNER JOIN users ON users.id = UserId
|
||||
WHERE GroupID = :GroupID
|
||||
ORDER BY Time DESC LIMIT 20 OFFSET $Offset",
|
||||
[":GroupID" => $GroupID]
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$LogsQuery = db::run(
|
||||
"SELECT groups_audit.*, users.username FROM groups_audit
|
||||
INNER JOIN users ON users.id = UserId
|
||||
WHERE GroupID = :GroupID AND Category = :Action
|
||||
ORDER BY Time DESC LIMIT 20 OFFSET $Offset",
|
||||
[":GroupID" => $GroupID, ":Action" => $Filter]
|
||||
);
|
||||
}
|
||||
|
||||
while($Log = $LogsQuery->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$Logs[] =
|
||||
[
|
||||
"Date" => date('j/n/y G:i', $Log->Time),
|
||||
"UserName" => $Log->username,
|
||||
"UserID" => $Log->UserID,
|
||||
"UserAvatar" => Thumbnails::GetAvatar($Log->UserID),
|
||||
"Rank" => Polygon::FilterText($Log->Rank),
|
||||
"Description" => Polygon::FilterText($Log->Description, false)
|
||||
];
|
||||
}
|
||||
|
||||
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $Pages, "items" => $Logs]));
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Groups");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST"]);
|
||||
|
||||
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
|
||||
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
|
||||
|
||||
if(!isset($_POST["RankLevel"])) api::respond(400, false, "RankLevel is not set");
|
||||
if(!is_numeric($_POST["RankLevel"])) api::respond(400, false, "RankLevel is not a number");
|
||||
|
||||
if(isset($_POST["Page"]) && !is_numeric($_POST["Page"])) api::respond(400, false, "Page is not a number");
|
||||
|
||||
$GroupID = $_POST["GroupID"] ?? false;
|
||||
$RankID = $_POST["RankLevel"] ?? false;
|
||||
$Members = [];
|
||||
|
||||
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
|
||||
if(!Groups::GetRankInfo($GroupID, $RankID)) api::respond(200, false, "Group rank does not exist");
|
||||
|
||||
$MemberCount = db::run(
|
||||
"SELECT COUNT(*) FROM groups_members WHERE GroupID = :GroupID AND Rank = :RankID AND NOT Pending",
|
||||
[":GroupID" => $GroupID, ":RankID" => $RankID]
|
||||
)->fetchColumn();
|
||||
|
||||
$Pagination = Pagination($_POST["Page"] ?? 1, $MemberCount, 12);
|
||||
|
||||
if($Pagination->Pages == 0) api::respond(200, true, "This group does not have any members of this rank.");
|
||||
|
||||
$MembersQuery = db::run(
|
||||
"SELECT users.username, users.id, Rank FROM groups_members
|
||||
INNER JOIN users ON users.id = groups_members.UserID
|
||||
WHERE GroupID = :GroupID AND Rank = :RankID AND NOT Pending
|
||||
ORDER BY Joined DESC LIMIT 12 OFFSET :Offset",
|
||||
[":GroupID" => $GroupID, ":RankID" => $RankID, ":Offset" => $Pagination->Offset]
|
||||
);
|
||||
|
||||
while($Member = $MembersQuery->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$Members[] =
|
||||
[
|
||||
"UserName" => $Member->username,
|
||||
"UserID" => $Member->id,
|
||||
"RoleLevel" => $Member->Rank,
|
||||
"Avatar" => Thumbnails::GetAvatar($Member->id)
|
||||
];
|
||||
}
|
||||
|
||||
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $Pagination->Pages, "count" => $MemberCount, "items" => $Members]));
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Groups");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST"]);
|
||||
|
||||
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
|
||||
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
|
||||
|
||||
if(!isset($_POST["Type"])) api::respond(400, false, "Type is not set");
|
||||
if(!in_array($_POST["Type"], ["Pending Allies", "Allies", "Enemies"])) api::respond(400, false, "Type is not valid");
|
||||
|
||||
if(isset($_POST["Page"]) && !is_numeric($_POST["Page"])) api::respond(400, false, "Page is not a number");
|
||||
|
||||
$GroupID = $_POST["GroupID"] ?? false;
|
||||
$Type = $_POST["Type"] ?? false;
|
||||
$Page = $_POST["Page"] ?? 1;
|
||||
$Groups = [];
|
||||
|
||||
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
|
||||
|
||||
if($Type == "Pending Allies")
|
||||
{
|
||||
if(!SESSION) api::respond(200, false, "You are not allowed to get this group's pending allies");
|
||||
$MyRank = Groups::GetUserRank(SESSION["user"]["id"], $GroupID);
|
||||
if(!$MyRank->Permissions->CanManageRelationships) api::respond(200, false, "You are not allowed to get this group's pending allies");
|
||||
|
||||
$GroupsCount = db::run(
|
||||
"SELECT COUNT(*) FROM groups_relationships WHERE Recipient = :GroupID AND Type = \"Allies\" AND Status = 0",
|
||||
[":GroupID" => $GroupID]
|
||||
)->fetchColumn();
|
||||
}
|
||||
else
|
||||
{
|
||||
$GroupsCount = db::run(
|
||||
"SELECT COUNT(*) FROM groups_relationships WHERE :GroupID IN (Declarer, Recipient) AND Type = :Type AND Status = 1",
|
||||
[":GroupID" => $GroupID, ":Type" => $Type]
|
||||
)->fetchColumn();
|
||||
}
|
||||
|
||||
$Pages = ceil($GroupsCount/12);
|
||||
$Offset = ($Page - 1)*12;
|
||||
|
||||
if(!$Pages) api::respond(200, true, "This group does not have any $Type");
|
||||
|
||||
if($Type == "Pending Allies")
|
||||
{
|
||||
$GroupsQuery = db::run(
|
||||
"SELECT groups.name, groups.id, groups.emblem,
|
||||
(SELECT COUNT(*) FROM groups_members WHERE GroupID = groups.id AND NOT Pending) AS MemberCount
|
||||
FROM groups_relationships
|
||||
INNER JOIN groups ON groups.id = (CASE WHEN Declarer = :GroupID THEN Recipient ELSE Declarer END)
|
||||
WHERE Recipient = :GroupID AND Type = \"Allies\" AND Status = 0
|
||||
ORDER BY Declared DESC LIMIT 12 OFFSET $Offset",
|
||||
[":GroupID" => $GroupID]
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$GroupsQuery = db::run(
|
||||
"SELECT groups.name, groups.id, groups.emblem,
|
||||
(SELECT COUNT(*) FROM groups_members WHERE GroupID = groups.id AND NOT Pending) AS MemberCount
|
||||
FROM groups_relationships
|
||||
INNER JOIN groups ON groups.id = (CASE WHEN Declarer = :GroupID THEN Recipient ELSE Declarer END)
|
||||
WHERE :GroupID IN (Declarer, Recipient) AND Type = :Type AND Status = 1
|
||||
ORDER BY Established DESC LIMIT 12 OFFSET $Offset",
|
||||
[":GroupID" => $GroupID, ":Type" => $Type]
|
||||
);
|
||||
}
|
||||
|
||||
while($Group = $GroupsQuery->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$Groups[] =
|
||||
[
|
||||
"Name" => Polygon::FilterText($Group->name),
|
||||
"ID" => $Group->id,
|
||||
"MemberCount" => $Group->MemberCount,
|
||||
"Emblem" => Thumbnails::GetAssetFromID($Group->emblem)
|
||||
];
|
||||
}
|
||||
|
||||
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $Pages, "items" => $Groups]));
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Groups");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
api::initialize(["method" => "POST"]);
|
||||
|
||||
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
|
||||
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
|
||||
|
||||
if(isset($_POST["Page"]) && !is_numeric($_POST["Page"])) api::respond(400, false, "Page is not a number");
|
||||
|
||||
$GroupID = $_POST["GroupID"] ?? false;
|
||||
$Wall = [];
|
||||
|
||||
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
|
||||
|
||||
if(SESSION) $Rank = Groups::GetUserRank(SESSION["user"]["id"], $GroupID);
|
||||
else $Rank = Groups::GetRankInfo($GroupID, 0);
|
||||
|
||||
if(!$Rank->Permissions->CanViewGroupWall) api::respond(200, false, "You are not allowed to view this group wall");
|
||||
|
||||
$PostCount = db::run(
|
||||
"SELECT COUNT(*) FROM groups_wall WHERE GroupID = :GroupID AND NOT Deleted",
|
||||
[":GroupID" => $GroupID]
|
||||
)->fetchColumn();
|
||||
|
||||
$Pagination = Pagination($_POST["Page"] ?? 1, $PostCount, 15);
|
||||
|
||||
if($Pagination->Pages == 0) api::respond(200, true, "This group does not have any wall posts.");
|
||||
|
||||
$PostQuery = db::run(
|
||||
"SELECT groups_wall.id, users.username AS PosterName, PosterID, Content, TimePosted FROM groups_wall
|
||||
INNER JOIN users ON users.id = PosterID WHERE GroupID = :GroupID AND NOT Deleted
|
||||
ORDER BY TimePosted DESC LIMIT 15 OFFSET :Offset",
|
||||
[":GroupID" => $GroupID, ":Offset" => $Pagination->Offset]
|
||||
);
|
||||
|
||||
while($Post = $PostQuery->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$Wall[] =
|
||||
[
|
||||
"id" => $Post->id,
|
||||
"username" => $Post->PosterName,
|
||||
"userid" => $Post->PosterID,
|
||||
"content" => nl2br(Polygon::FilterText($Post->Content)),
|
||||
"time" => date('j/n/Y g:i:s A', $Post->TimePosted),
|
||||
"avatar" => Thumbnails::GetAvatar($Post->PosterID)
|
||||
];
|
||||
}
|
||||
|
||||
die(json_encode(["status" => 200, "success" => true, "message" => "OK", "pages" => $Pagination->Pages, "count" => $PostCount, "items" => $Wall]));
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Groups");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
|
||||
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
|
||||
|
||||
$GroupID = $_POST["GroupID"] ?? false;
|
||||
|
||||
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
|
||||
if(Groups::CheckIfUserInGroup(SESSION["user"]["id"], $GroupID)) api::respond(200, false, "You are already in this group");
|
||||
|
||||
if(Groups::GetUserGroups(SESSION["user"]["id"])->rowCount() >= 20) api::respond(200, false, "You have reached the maximum number of groups");
|
||||
|
||||
$RateLimit = db::run("SELECT Joined FROM groups_members WHERE UserID = :UserID AND Joined+300 > UNIX_TIMESTAMP()", [":UserID" => SESSION["user"]["id"]]);
|
||||
if($RateLimit->rowCount())
|
||||
api::respond(200, false, "Please wait ".GetReadableTime($RateLimit->fetchColumn(), ["RelativeTime" => "5 minutes"])." before joining a new group");
|
||||
|
||||
$RankLevel = db::run(
|
||||
"SELECT Rank FROM groups_ranks WHERE GroupID = :GroupID AND Rank != 0 ORDER BY Rank ASC LIMIT 1",
|
||||
[":GroupID" => $GroupID]
|
||||
)->fetchColumn();
|
||||
|
||||
db::run(
|
||||
"INSERT INTO groups_members (GroupID, UserID, Rank, Joined) VALUES (:GroupID, :UserID, :RankLevel, UNIX_TIMESTAMP())",
|
||||
[":GroupID" => $GroupID, ":UserID" => SESSION["user"]["id"], ":RankLevel" => $RankLevel]
|
||||
);
|
||||
|
||||
api::respond(200, true, "OK");
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Groups");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
|
||||
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
|
||||
|
||||
$GroupID = $_POST["GroupID"] ?? false;
|
||||
$GroupInfo = Groups::GetGroupInfo($GroupID);
|
||||
|
||||
if(!$GroupInfo) api::respond(200, false, "Group does not exist");
|
||||
if($GroupInfo->creator == SESSION["user"]["id"]) api::respond(200, false, "You are the creator of this group");
|
||||
if(!Groups::CheckIfUserInGroup(SESSION["user"]["id"], $GroupID)) api::respond(200, false, "You are not in this group");
|
||||
|
||||
db::run(
|
||||
"DELETE FROM groups_members WHERE GroupID = :GroupID AND UserID = :UserID",
|
||||
[":GroupID" => $GroupID, ":UserID" => SESSION["user"]["id"]]
|
||||
);
|
||||
|
||||
api::respond(200, true, "OK");
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Groups");
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
Polygon::ImportClass("Discord");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
|
||||
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
|
||||
|
||||
if(!isset($_POST["Content"])) api::respond(400, false, "Content is not set");
|
||||
|
||||
$GroupID = $_POST["GroupID"] ?? false;
|
||||
$Content = $_POST["Content"] ?? false;
|
||||
$GroupInfo = Groups::GetGroupInfo($GroupID);
|
||||
|
||||
if(!$GroupInfo) api::respond(200, false, "Group does not exist");
|
||||
|
||||
$Rank = Groups::GetUserRank(SESSION["user"]["id"], $GroupID);
|
||||
|
||||
if(!$Rank->Permissions->CanPostGroupStatus) api::respond(200, false, "You are not allowed to post on this group wall");
|
||||
|
||||
if(strlen($Content) < 3) api::respond(200, false, "Group shout must be at least 3 characters long");
|
||||
if(strlen($Content) > 255) api::respond(200, false, "Group shout can not be longer than 64 characters");
|
||||
|
||||
$LastPost = db::run(
|
||||
"SELECT timestamp FROM feed WHERE groupId = :GroupID AND userId = :UserID AND timestamp+300 > UNIX_TIMESTAMP()",
|
||||
[":GroupID" => $GroupID, ":UserID" => SESSION["user"]["id"]]
|
||||
);
|
||||
|
||||
if($LastPost->rowCount())
|
||||
api::respond(200, false, "Please wait ".GetReadableTime($LastPost->fetchColumn(), ["RelativeTime" => "5 minutes"])." before posting a group shout");
|
||||
|
||||
Groups::LogAction(
|
||||
$GroupID, "Post Shout",
|
||||
sprintf(
|
||||
"<a href=\"/user?ID=%d\">%s</a> changed the group status to: %s",
|
||||
SESSION["user"]["id"], SESSION["user"]["username"], htmlspecialchars($Content)
|
||||
)
|
||||
);
|
||||
|
||||
db::run(
|
||||
"INSERT INTO feed (groupId, userId, text, timestamp) VALUES (:GroupID, :UserID, :Content, UNIX_TIMESTAMP())",
|
||||
[":GroupID" => $GroupID, ":UserID" => SESSION["user"]["id"], ":Content" => $Content]
|
||||
);
|
||||
|
||||
Discord::SendToWebhook(
|
||||
[
|
||||
"username" => $GroupInfo->name,
|
||||
"content" => $Content."\n(Posted by ".SESSION["user"]["username"].")",
|
||||
"avatar_url" => Thumbnails::GetAssetFromID($GroupInfo->emblem)
|
||||
],
|
||||
Discord::WEBHOOK_KUSH
|
||||
);
|
||||
|
||||
api::respond(200, true, "OK");
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Groups");
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
if(!isset($_POST["GroupID"])) api::respond(400, false, "GroupID is not set");
|
||||
if(!is_numeric($_POST["GroupID"])) api::respond(400, false, "GroupID is not a number");
|
||||
|
||||
if(!isset($_POST["Content"])) api::respond(400, false, "Content is not set");
|
||||
|
||||
$GroupID = $_POST["GroupID"] ?? false;
|
||||
$Content = $_POST["Content"] ?? false;
|
||||
|
||||
if(!Groups::GetGroupInfo($GroupID)) api::respond(200, false, "Group does not exist");
|
||||
|
||||
$Rank = Groups::GetUserRank(SESSION["user"]["id"], $GroupID);
|
||||
|
||||
if(!$Rank->Permissions->CanPostOnGroupWall) api::respond(200, false, "You are not allowed to post on this group wall");
|
||||
|
||||
if(strlen($Content) < 3) api::respond(200, false, "Wall post must be at least 3 characters long");
|
||||
if(strlen($Content) > 255) api::respond(200, false, "Wall post can not be longer than 255 characters");
|
||||
|
||||
$LastPost = db::run(
|
||||
"SELECT TimePosted FROM groups_wall
|
||||
WHERE GroupID = :GroupID AND PosterID = :UserID AND TimePosted+60 > UNIX_TIMESTAMP()
|
||||
ORDER BY TimePosted DESC LIMIT 1",
|
||||
[":GroupID" => $GroupID, ":UserID" => SESSION["user"]["id"]]
|
||||
);
|
||||
|
||||
if(SESSION["user"]["id"] != 1 && $LastPost->rowCount())
|
||||
api::respond(200, false, "Please wait ".(60-(time()-$LastPost->fetchColumn()))." seconds before posting a new wall post");
|
||||
|
||||
db::run(
|
||||
"INSERT INTO groups_wall (GroupID, PosterID, Content, TimePosted) VALUES (:GroupID, :UserID, :Content, UNIX_TIMESTAMP())",
|
||||
[":GroupID" => $GroupID, ":UserID" => SESSION["user"]["id"], ":Content" => $Content]
|
||||
);
|
||||
|
||||
api::respond(200, true, "OK");
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
Polygon::ImportClass("Thumbnails");
|
||||
|
||||
$categories =
|
||||
[
|
||||
0=>"Bricks",
|
||||
1=>"Robots",
|
||||
2=>"Chassis",
|
||||
3=>"Furniture",
|
||||
4=>"Roads",
|
||||
5=>"Billboards",
|
||||
6=>"Game Objects",
|
||||
"MyDecals"=>"My Decals",
|
||||
"FreeDecals"=>"Free Decals",
|
||||
"MyModels"=>"My Models",
|
||||
"FreeModels"=>"Free Models"
|
||||
];
|
||||
|
||||
$category = isset($_POST['category']) && isset($categories[$_POST['category']]) ? $_POST['category'] : "FreeModels";
|
||||
$categoryText = $categories[$category];
|
||||
$type = strpos($category, "Decals") ? 13 : 10;
|
||||
$page = $_POST['page'] ?? 1;
|
||||
$keywd = $_POST['keyword'] ?? false;
|
||||
$keywd_sql = $keywd ? "%".$keywd."%" : "%";
|
||||
|
||||
if(is_numeric($category)) //static category
|
||||
{
|
||||
//$query = $pdo->prepare("SELECT COUNT(*) FROM catalog_items WHERE toolboxCategory = :category");
|
||||
//$query->bindParam(":category", $categoryText, PDO::PARAM_STR);
|
||||
}
|
||||
else //dynamic category - user assets, catalog assets
|
||||
{
|
||||
if(SESSION && strpos($categoryText, "My") !== false) //get assets from inventory
|
||||
{
|
||||
$userId = SESSION["user"]["id"];
|
||||
$query = $pdo->prepare("SELECT COUNT(*) FROM assets WHERE type = :type AND approved = 1 AND id IN (SELECT assetId FROM ownedAssets WHERE userId = :uid)");
|
||||
$query->bindParam(":uid", $userId, PDO::PARAM_INT);
|
||||
}
|
||||
else //get assets from catalog
|
||||
{
|
||||
$query = $pdo->prepare("SELECT COUNT(*) FROM assets WHERE type = :type AND approved = 1 AND (name LIKE :q OR description LIKE :q)");
|
||||
$query->bindParam(":q", $keywd_sql, PDO::PARAM_STR);
|
||||
}
|
||||
$query->bindParam(":type", $type, PDO::PARAM_INT);
|
||||
}
|
||||
|
||||
$query->execute();
|
||||
$items = $query->fetchColumn();
|
||||
$pages = ceil($items/20);
|
||||
$offset = ($page - 1)*20;
|
||||
|
||||
if(is_numeric($category)) //static category
|
||||
{
|
||||
//$query = $pdo->prepare("SELECT * FROM catalog_items WHERE toolboxCategory = :category ORDER BY id ASC LIMIT 20 OFFSET :offset");
|
||||
//$query->bindParam(":category", $categoryText, PDO::PARAM_STR);
|
||||
}
|
||||
else //dynamic category - user assets, catalog assets
|
||||
{
|
||||
if(strpos($categoryText, "My") !== false) //get assets from inventory
|
||||
{
|
||||
$userId = SESSION["user"]["id"];
|
||||
$query = $pdo->prepare("SELECT assets.* FROM ownedAssets INNER JOIN assets ON assets.id = assetId WHERE userId = :uid AND assets.type = :type ORDER BY timestamp DESC LIMIT 20 OFFSET :offset"); //all of this just to order by time bought...
|
||||
$query->bindParam(":uid", $userId, PDO::PARAM_INT);
|
||||
}
|
||||
else //get assets from catalog
|
||||
{
|
||||
$query = $pdo->prepare("SELECT * FROM assets WHERE type = :type AND approved = 1 AND (name LIKE :q OR description LIKE :q) ORDER BY updated DESC LIMIT 20 OFFSET :offset");
|
||||
$query->bindParam(":q", $keywd_sql, PDO::PARAM_STR);
|
||||
$query->bindParam(":q2", $keywd_sql, PDO::PARAM_STR);
|
||||
}
|
||||
$query->bindParam(":type", $type, PDO::PARAM_INT);
|
||||
}
|
||||
|
||||
$query->bindParam(":offset", $offset, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
?>
|
||||
<div id="ToolBoxPage">
|
||||
<div>
|
||||
<?php if($pages>1) { ?>
|
||||
<div id="pNavigation" style="display:table">
|
||||
<div class="Navigation">
|
||||
<div id="Previous">
|
||||
<a href="#" onclick="getToolbox('<?=$category?>', '<?=$keywd?>', <?=$page-1?>)" id="PreviousPage" <?=$page <= 1 ? 'style="visibility:hidden"':''?>><span class="NavigationIndicators"><<</span>
|
||||
Prev</a>
|
||||
</div>
|
||||
<div id="Next">
|
||||
<a href="#" onclick="getToolbox('<?=$category?>', '<?=$keywd?>', <?=$page+1?>)" id="NextPage" <?=$page >= $pages ? 'style="visibility:hidden"':''?>>Next <span class="NavigationIndicators">>></span></a>
|
||||
</div>
|
||||
<div id="Location">
|
||||
<span id="PagerLocation"><?=number_format((($page-1)*20)+1)?>-<?=number_format($page*20)?> of <?=number_format($items)?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<div id="ToolboxItems">
|
||||
<?php while($row = $query->fetch(PDO::FETCH_OBJ)) { $name = Polygon::FilterText($row->name); ?>
|
||||
<a class="ToolboxItem" title="<?=$name?>" href="javascript:insertContent(<?=$row->id?>)" ondragstart="dragRBX(<?=$row->id?>)" onmouseover="this.style.borderStyle='outset'" onmouseout="this.style.borderStyle='solid'" style="border-style: solid;display:inline-block;height:60px;width:60px;cursor:pointer;">
|
||||
<img width="60" src="<?=Thumbnails::GetAsset($row)?>" border="0" id="img" alt="<?=$name?>">
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php if($pages>1) { ?>
|
||||
<div id="pNavigation" style="display:table">
|
||||
<div class="Navigation">
|
||||
<div id="Previous">
|
||||
<a href="#" onclick="getToolbox('<?=$category?>', '<?=$keywd?>', <?=$page-1?>)" id="PreviousPage" <?=$page <= 1 ? 'style="visibility:hidden"':''?>><span class="NavigationIndicators"><<</span>
|
||||
Prev</a>
|
||||
</div>
|
||||
<div id="Next">
|
||||
<a href="#" onclick="getToolbox('<?=$category?>', '<?=$keywd?>', <?=$page+1?>)" id="NextPage" <?=$page >= $pages ? 'style="visibility:hidden"':''?>>Next <span class="NavigationIndicators">>></span></a>
|
||||
</div>
|
||||
<div id="Location">
|
||||
<span id="PagerLocation"><?=number_format((($page-1)*20)+1)?>-<?=number_format($page*20)?> of <?=number_format($items)?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
|
||||
$UserId = SESSION["user"]["id"];
|
||||
$page = $_POST['page'] ?? 1;
|
||||
|
||||
$query = $pdo->prepare("SELECT COUNT(*) FROM messages WHERE ReceiverID = :uid AND TimeArchived = 0");
|
||||
$query->bindParam(":uid", $UserId, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
|
||||
$pages = ceil($query->fetchColumn()/18);
|
||||
$offset = ($page - 1)*18;
|
||||
|
||||
if(!$pages) api::respond(200, true, "Messages you receive from other users will be shown here.");
|
||||
|
||||
$query = $pdo->prepare("SELECT * FROM messages WHERE ReceiverID = :uid AND TimeArchived = 0 LIMIT 13 OFFSET :offset");
|
||||
$query->bindParam(":uid", $UserId, PDO::PARAM_INT);
|
||||
$query->bindParam(":offset", $offset, PDO::PARAM_INT);
|
||||
$query->execute();
|
||||
|
||||
$messages = [];
|
||||
|
||||
while($row = $query->fetch(PDO::FETCH_OBJ))
|
||||
{
|
||||
$messages[] =
|
||||
[
|
||||
"Username" => Users::GetNameFromID($row->SenderID),
|
||||
"UserId" => $row->SenderID,
|
||||
"MessageId" => $row->ID,
|
||||
"Subject" => Polygon::FilterText($row->Subject, true, false),
|
||||
"TimeSent" => date('d M Y h:m a', $row->TimeSent),
|
||||
"TimeRead" => $row->TimeRead
|
||||
];
|
||||
}
|
||||
|
||||
api::respond_custom(["status" => 200, "success" => true, "message" => "OK", "messages" => $messages, "pages" => $pages]);
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT'].'/api/private/core.php';
|
||||
|
||||
api::initialize(["method" => "POST", "logged_in" => true, "secure" => true]);
|
||||
Polygon::ImportClass("Messages");
|
||||
|
||||
$isReply = false;
|
||||
|
||||
|
||||
$messageId = $_POST["messageId"] ?? false;
|
||||
|
||||
if($messageId)
|
||||
$isReply = true;
|
||||
|
||||
if($isReply) {
|
||||
$replyInfo = Messages::getMessageInfoFromId($messageId);
|
||||
if(!$replyInfo) api::respond(400, false, "Invalid Request");
|
||||
}
|
||||
|
||||
|
||||
if(!isset($_POST["subject"]) && !$isReply || !isset($_POST["body"]) || !isset($_POST["recipientId"])) api::respond(400, false, "Invalid Request");
|
||||
|
||||
|
||||
if(!$isReply) {
|
||||
if(!trim($_POST["subject"])) api::respond(400, false, "You cannot leave the subject empty");
|
||||
if(strlen($_POST["subject"] > 128) || strlen($_POST["subject"]) < 2) api::respond(400, false, "Message subject must be under 2-128 characters long.");
|
||||
}
|
||||
|
||||
if(!trim($_POST["body"])) api::respond(400, false, "You cannot leave the body empty");
|
||||
if(strlen($_POST["body"] > 768) || strlen($_POST["body"]) < 3) api::respond(400, false, "Message body must be under 3-768 characters long.");
|
||||
|
||||
$RecipientId = $_POST["recipientId"];
|
||||
$UserId = SESSION["user"]["id"];
|
||||
$RecipientInfo = Users::GetInfoFromID($RecipientId);
|
||||
if(!$RecipientInfo) api::respond(400, false, "Invalid Request");
|
||||
|
||||
if($isReply) {
|
||||
$Subject = htmlspecialchars("RE: " . $replyInfo->Subject);
|
||||
} else {
|
||||
$Subject = htmlspecialchars($_POST["subject"]);
|
||||
}
|
||||
|
||||
$Body = htmlspecialchars($_POST["body"]);
|
||||
|
||||
db::run("INSERT INTO messages (SenderID, ReceiverID, Subject, Body, TimeSent, TimeArchived, TimeRead) VALUES (:sid, :rid, :sub, :body, UNIX_TIMESTAMP(), 0, 0)",
|
||||
[":sid" => $UserId, ":rid" => $RecipientId, ":sub" => $Subject, ":body" => $Body]);
|
||||
|
||||
api::respond(200, true, "Message sent.");
|
||||
|
|
@ -0,0 +1,327 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT']."/api/private/core.php";
|
||||
Polygon::ImportClass("RBXClient");
|
||||
|
||||
if (!Polygon::IsGameserverAuthorized()) PageBuilder::errorCode(404);
|
||||
|
||||
$ScriptArgs = (object)
|
||||
[
|
||||
"jobId" => $_GET["jobId"] ?? "nil",
|
||||
"placeId" => $_GET["placeId"] ?? "nil",
|
||||
"port" => $_GET["port"] ?? "nil",
|
||||
"maxPlayers" => $_GET["maxPlayers"] ?? "nil",
|
||||
];
|
||||
|
||||
header("content-type: text/plain; charset=utf-8");
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
local injectScriptAssetID = nil
|
||||
local libraryRegistrationScriptAssetID = nil
|
||||
|
||||
local jobId = "<?=$ScriptArgs->jobId?>"
|
||||
local placeId = <?=$ScriptArgs->placeId?>
|
||||
local port = <?=$ScriptArgs->port?>
|
||||
local maxPlayers = <?=$ScriptArgs->maxPlayers?>
|
||||
|
||||
local url = "http://<?=$_SERVER["HTTP_HOST"]?>"
|
||||
local servicesUrl = url
|
||||
local access = "<?=SITE_CONFIG["keys"]["GameserverAccess"]?>"
|
||||
|
||||
local PolygonTickets = {}
|
||||
|
||||
-- StartGame --
|
||||
pcall(function() game:GetService("ScriptContext"):AddStarterScript(injectScriptAssetID) end)
|
||||
game:GetService("Visit"):SetUploadUrl("")
|
||||
game:GetService("RunService"):Run()
|
||||
|
||||
-- REQUIRES: StartGanmeSharedArgs.txt
|
||||
-- REQUIRES: MonitorGameStatus.txt
|
||||
|
||||
------------------- UTILITY FUNCTIONS --------------------------
|
||||
|
||||
function waitForChild(parent, childName)
|
||||
while true do
|
||||
local child = parent:findFirstChild(childName)
|
||||
if child then
|
||||
return child
|
||||
end
|
||||
parent.ChildAdded:wait()
|
||||
end
|
||||
end
|
||||
|
||||
-- returns the player object that killed this humanoid
|
||||
-- returns nil if the killer is no longer in the game
|
||||
function getKillerOfHumanoidIfStillInGame(humanoid)
|
||||
|
||||
-- check for kill tag on humanoid - may be more than one - todo: deal with this
|
||||
local tag = humanoid:findFirstChild("creator")
|
||||
|
||||
-- find player with name on tag
|
||||
if tag then
|
||||
local killer = tag.Value
|
||||
if killer.Parent then -- killer still in game
|
||||
return killer
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
-- send kill and death stats when a player dies
|
||||
function onDied(victim, humanoid)
|
||||
local killer = getKillerOfHumanoidIfStillInGame(humanoid)
|
||||
local victorId = 0
|
||||
if killer then
|
||||
victorId = killer.userId
|
||||
print("STAT: kill by " .. victorId .. " of " .. victim.userId)
|
||||
game:HttpGet(url .. "/Game/Knockouts.ashx?UserID=" .. victorId .. "&" .. access)
|
||||
print("STAT: death of " .. victim.userId .. " by " .. victorId)
|
||||
game:HttpGet(url .. "/Game/Wipeouts.ashx?UserID=" .. victim.userId .. "&" .. access)
|
||||
end
|
||||
end
|
||||
|
||||
-- This code might move to C++
|
||||
function characterRessurection(player)
|
||||
if player.Character then
|
||||
local humanoid = player.Character.Humanoid
|
||||
humanoid.Died:connect(function() wait(5) player:LoadCharacter() end)
|
||||
end
|
||||
end
|
||||
|
||||
-----------------------------------END UTILITY FUNCTIONS -------------------------
|
||||
|
||||
-----------------------------------"CUSTOM" SHARED CODE----------------------------------
|
||||
|
||||
pcall(function() settings().Network.UseInstancePacketCache = true end)
|
||||
pcall(function() settings().Network.UsePhysicsPacketCache = true end)
|
||||
--pcall(function() settings()["Task Scheduler"].PriorityMethod = Enum.PriorityMethod.FIFO end)
|
||||
pcall(function() settings()["Task Scheduler"].PriorityMethod = Enum.PriorityMethod.AccumulatedError end)
|
||||
|
||||
--settings().Network.PhysicsSend = 1 -- 1==RoundRobin
|
||||
pcall(function() settings().Network.PhysicsSend = Enum.PhysicsSendMethod.ErrorComputation2 end)
|
||||
pcall(function() settings().Network.ExperimentalPhysicsEnabled = true end)
|
||||
pcall(function() settings().Network.WaitingForCharacterLogRate = 100 end)
|
||||
pcall(function() settings().Diagnostics:LegacyScriptMode() end)
|
||||
|
||||
-----------------------------------START GAME SHARED SCRIPT------------------------------
|
||||
|
||||
local assetId = placeId -- might be able to remove this now
|
||||
|
||||
local scriptContext = game:GetService('ScriptContext')
|
||||
pcall(function() scriptContext:AddStarterScript(libraryRegistrationScriptAssetID) end)
|
||||
scriptContext.ScriptsDisabled = true
|
||||
|
||||
pcall(function() game:SetPlaceID(assetId, false) end)
|
||||
game:GetService("ChangeHistoryService"):SetEnabled(false)
|
||||
|
||||
-- establish this peer as the Server
|
||||
local ns = game:GetService("NetworkServer")
|
||||
|
||||
if url~=nil then
|
||||
pcall(function() game:GetService("Players"):SetAbuseReportUrl(url .. "/AbuseReport/InGameChatHandler.ashx") end)
|
||||
pcall(function() game:GetService("ScriptInformationProvider"):SetAssetUrl(url .. "/Asset/") end)
|
||||
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(url .. "/") end)
|
||||
-- pcall(function() game:GetService("Players"):SetChatFilterUrl(url .. "/Game/ChatFilter.ashx") end)
|
||||
|
||||
game:GetService("BadgeService"):SetPlaceId(placeId)
|
||||
if access~=nil then
|
||||
game:GetService("BadgeService"):SetAwardBadgeUrl(url .. "/Game/Badge/AwardBadge.ashx?UserID=%d&BadgeID=%d&PlaceID=%d&" .. access)
|
||||
game:GetService("BadgeService"):SetHasBadgeUrl(url .. "/Game/Badge/HasBadge.ashx?UserID=%d&BadgeID=%d&" .. access)
|
||||
game:GetService("BadgeService"):SetIsBadgeDisabledUrl(url .. "/Game/Badge/IsBadgeDisabled.ashx?BadgeID=%d&PlaceID=%d&" .. access)
|
||||
|
||||
pcall(function() game:GetService("FriendService"):SetMakeFriendUrl(servicesUrl .. "/Friend/CreateFriend?firstUserId=%d&secondUserId=%d&jobId=" .. jobId .. "&" .. access) end)
|
||||
pcall(function() game:GetService("FriendService"):SetBreakFriendUrl(servicesUrl .. "/Friend/BreakFriend?firstUserId=%d&secondUserId=%d&jobId=" .. jobId .. "&" .. access) end)
|
||||
pcall(function() game:GetService("FriendService"):SetGetFriendsUrl(servicesUrl .. "/Friend/AreFriends?userId=%d&" .. access) end)
|
||||
end
|
||||
game:GetService("BadgeService"):SetIsBadgeLegalUrl("")
|
||||
game:GetService("InsertService"):SetBaseSetsUrl(url .. "/Game/Tools/InsertAsset.ashx?nsets=10&type=base")
|
||||
game:GetService("InsertService"):SetUserSetsUrl(url .. "/Game/Tools/InsertAsset.ashx?nsets=20&type=user&userid=%d")
|
||||
game:GetService("InsertService"):SetCollectionUrl(url .. "/Game/Tools/InsertAsset.ashx?sid=%d")
|
||||
game:GetService("InsertService"):SetAssetUrl(url .. "/Asset/?id=%d")
|
||||
game:GetService("InsertService"):SetAssetVersionUrl(url .. "/Asset/?assetversionid=%d")
|
||||
|
||||
pcall(function() loadfile(url .. "/Game/LoadPlaceInfo.ashx?PlaceId=" .. placeId)() end)
|
||||
|
||||
pcall(function()
|
||||
if access then
|
||||
loadfile(url .. "/Game/PlaceSpecificScript.ashx?PlaceId=" .. placeId .. "&" .. access)()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
pcall(function() game:GetService("NetworkServer"):SetIsPlayerAuthenticationRequired(false) end)
|
||||
settings().Diagnostics.LuaRamLimit = 0
|
||||
--settings().Network:SetThroughputSensitivity(0.08, 0.01)
|
||||
--settings().Network.SendRate = 35
|
||||
--settings().Network.PhysicsSend = 0 -- 1==RoundRobin
|
||||
|
||||
--shared["__time"] = 0
|
||||
--game:GetService("RunService").Stepped:connect(function (time) shared["__time"] = time end)
|
||||
|
||||
|
||||
|
||||
|
||||
if placeId~=nil and url~=nil then
|
||||
-- listen for the death of a Player
|
||||
function createDeathMonitor(player)
|
||||
-- we don't need to clean up old monitors or connections since the Character will be destroyed soon
|
||||
if player.Character then
|
||||
local humanoid = waitForChild(player.Character, "Humanoid")
|
||||
humanoid.Died:connect(
|
||||
function ()
|
||||
onDied(player, humanoid)
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
-- listen to all Players' Characters
|
||||
game:GetService("Players").ChildAdded:connect(
|
||||
function (player)
|
||||
createDeathMonitor(player)
|
||||
player.Changed:connect(
|
||||
function (property)
|
||||
if property=="Character" then
|
||||
createDeathMonitor(player)
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function InactivityHandler(time)
|
||||
wait(1)
|
||||
|
||||
if #game:GetService("Players"):GetChildren() > 0 then
|
||||
if time ~= nil then
|
||||
print("Inactive shutdown timer aborted")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if time == nil then
|
||||
print("Server is inactive, shutting down in 5 minutes")
|
||||
time = 0
|
||||
end
|
||||
|
||||
if time == 300 then
|
||||
game:Shutdown()
|
||||
end
|
||||
|
||||
InactivityHandler(time+1)
|
||||
end
|
||||
|
||||
game:GetService("Players").PlayerAdded:connect(function(player)
|
||||
print("Player " .. player.userId .. " added")
|
||||
|
||||
characterRessurection(player)
|
||||
player.Changed:connect(function(name)
|
||||
if name=="Character" then
|
||||
characterRessurection(player)
|
||||
end
|
||||
end)
|
||||
|
||||
--[[ if url and access and placeId and player and player.userId then
|
||||
game:HttpGet(url .. "/Game/ClientPresence.ashx?action=connect&" .. access .. "&Ticket=" .. player.PolygonTicket.Value)
|
||||
game:HttpGet(url .. "/Game/PlaceVisit.ashx?Ticket=" .. player.PolygonTicket .. "&" .. access)
|
||||
end --]]
|
||||
end)
|
||||
|
||||
|
||||
game:GetService("Players").PlayerRemoving:connect(function(player)
|
||||
print("Player " .. player.userId .. " leaving")
|
||||
|
||||
if url and access and placeId and player and player.userId and PolygonTickets[player.userId] ~= nil then
|
||||
game:HttpGet(url .. "/Game/ClientPresence.ashx?action=disconnect&" .. access .. "&Ticket=" .. PolygonTickets[player.userId])
|
||||
PolygonTickets[player.userId] = nil
|
||||
end
|
||||
|
||||
InactivityHandler()
|
||||
end)
|
||||
|
||||
-- this is already handled by the arbiter, but this is still here just in case
|
||||
game.Close:connect(function()
|
||||
game:HttpGet(url .. "/api/polygongs/update-job?" .. access .. "&JobID=" .. jobId .. "&Status=Closed")
|
||||
end)
|
||||
|
||||
if placeId~=nil and url~=nil then
|
||||
-- yield so that file load happens in the heartbeat thread
|
||||
wait()
|
||||
|
||||
-- load the game
|
||||
game:Load(url .. "/asset/?id=" .. placeId .. "&" .. access)
|
||||
end
|
||||
|
||||
ns.ChildAdded:connect(function(replicator)
|
||||
player = replicator:GetPlayer()
|
||||
|
||||
i = 0
|
||||
if player == nil then
|
||||
while wait(0.25) do
|
||||
player = replicator:GetPlayer()
|
||||
if player ~= nil then break end
|
||||
if i == 120 then
|
||||
print("[paclib] kicked incoming connection because could not get player")
|
||||
replicator:CloseConnection()
|
||||
return
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
if player.CharacterAppearance ~= url .. "/Asset/CharacterFetch.ashx?userId=" ..player.userId.. "&placeId=" .. placeId then
|
||||
replicator:CloseConnection()
|
||||
print("[paclib] kicked " .. player.Name .. " because player does not have correct character appearance for this server")
|
||||
print("[paclib] correct character appearance url: " .. url .. "/Asset/CharacterFetch.ashx?userId=" .. player.userId .. "&placeId=" .. placeId)
|
||||
print("[paclib] appearance that the server received: " .. player.CharacterAppearance)
|
||||
return
|
||||
end
|
||||
|
||||
if player:FindFirstChild("PolygonTicket") == nil then
|
||||
replicator:CloseConnection()
|
||||
print("[paclib] kicked " .. player.Name .. " because player does not have an authentication ticket")
|
||||
return
|
||||
end
|
||||
|
||||
-- todo - pass in membership value
|
||||
response = game:HttpGet(url .. "/api/polygongs/verify-player?Username=" .. player.Name .. "&UserID=" .. player.userId .. "&Ticket=" .. player.PolygonTicket.Value .. "&JobID=" .. jobId .. "&" .. access, true)
|
||||
if response ~= "True" then
|
||||
replicator:CloseConnection()
|
||||
print("[paclib] kicked " .. player.Name .. " because could not validate player")
|
||||
print("[paclib] validation handler returned: " .. response)
|
||||
return
|
||||
end
|
||||
|
||||
PolygonTickets[player.userId] = player.PolygonTicket.Value
|
||||
player.PolygonTicket:Remove()
|
||||
|
||||
print("[paclib] " .. player.Name .. " has been authenticated")
|
||||
|
||||
if url and access and placeId and player and player.userId then
|
||||
game:HttpGet(url .. "/Game/ClientPresence.ashx?action=connect&" .. access .. "&Ticket=" .. PolygonTickets[player.userId])
|
||||
game:HttpGet(url .. "/Game/PlaceVisit.ashx?Ticket=" .. PolygonTickets[player.userId] .. "&" .. access)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Now start the connection
|
||||
ns:Start(port, 15)
|
||||
|
||||
scriptContext:SetTimeout(10)
|
||||
scriptContext.ScriptsDisabled = false
|
||||
|
||||
--delay(1, function()
|
||||
-- loadfile(url .. "/analytics/GamePerfMonitor.ashx")(jobId, placeId)
|
||||
--end)
|
||||
|
||||
game:HttpGet(url .. "/api/polygongs/update-job?" .. access .. "&JobID=" .. jobId .. "&Status=Ready")
|
||||
|
||||
InactivityHandler()
|
||||
|
||||
wait(10)
|
||||
pcall(function() game:ToggleTools() end)
|
||||
|
||||
------------------------------END START GAME SHARED SCRIPT--------------------------
|
||||
|
||||
|
||||
<?php echo RBXClient::CryptSignScript(ob_get_clean());
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT']."/api/private/core.php";
|
||||
if (!Polygon::IsGameserverAuthorized()) PageBuilder::errorCode(404);
|
||||
|
||||
header("content-type: text/plain; charset=utf-8");
|
||||
|
||||
$GameserverID = api::GetParameter("POST", "GameserverID", "int");
|
||||
$CpuUsage = api::GetParameter("POST", "CpuUsage", "int");
|
||||
$AvailableMemory = api::GetParameter("POST", "AvailableMemory", "int");
|
||||
|
||||
db::run("
|
||||
UPDATE GameServers
|
||||
SET LastUpdated = UNIX_TIMESTAMP(), CpuUsage = :CpuUsage, AvailableMemory = :AvailableMemory
|
||||
WHERE ServerID = :GameserverID",
|
||||
[":CpuUsage" => $CpuUsage, ":AvailableMemory" => $AvailableMemory, ":GameserverID" => $GameserverID]
|
||||
);
|
||||
|
||||
echo "OK";
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT']."/api/private/core.php";
|
||||
if (!Polygon::IsGameserverAuthorized()) PageBuilder::errorCode(404);
|
||||
|
||||
header("content-type: text/plain; charset=utf-8");
|
||||
|
||||
$GameserverID = api::GetParameter("GET", "GameserverID", "int");
|
||||
$Online = api::GetParameter("GET", "Online", "int");
|
||||
|
||||
db::run(
|
||||
"UPDATE GameServers SET Online = :Online, ActiveJobs = 0, LastUpdated = UNIX_TIMESTAMP() WHERE ServerID = :GameserverID",
|
||||
[":Online" => $Online, ":GameserverID" => $GameserverID]
|
||||
);
|
||||
|
||||
echo "OK";
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT']."/api/private/core.php";
|
||||
Polygon::ImportClass("Games");
|
||||
if (!Polygon::IsGameserverAuthorized()) PageBuilder::errorCode(404);
|
||||
|
||||
header("content-type: text/plain; charset=utf-8");
|
||||
|
||||
function AddSQLParameter($Name, $Value)
|
||||
{
|
||||
global $Parameters;
|
||||
global $ParametersSQL;
|
||||
|
||||
$ParametersSQL .= ", {$Name} = :{$Name}";
|
||||
$Parameters[":{$Name}"] = $Value;
|
||||
}
|
||||
|
||||
$JobID = api::GetParameter("GET", "JobID", "string");
|
||||
$Status = api::GetParameter("GET", "Status", "string");
|
||||
$MachineAddress = api::GetParameter("GET", "MachineAddress", "string", false);
|
||||
$ServerPort = api::GetParameter("GET", "ServerPort", "int", false);
|
||||
|
||||
$ParametersSQL = "LastUpdated = UNIX_TIMESTAMP()";
|
||||
$Parameters = [":JobID" => $JobID];
|
||||
|
||||
if ($Status !== false) AddSQLParameter("Status", $Status);
|
||||
if ($MachineAddress !== false) AddSQLParameter("MachineAddress", $MachineAddress);
|
||||
if ($ServerPort !== false) AddSQLParameter("ServerPort", $ServerPort);
|
||||
|
||||
$JobInfo = Games::GetJobInfo($JobID);
|
||||
|
||||
// update the job with the specified parameters
|
||||
db::run(
|
||||
"UPDATE GameJobs SET {$ParametersSQL} WHERE JobID = :JobID",
|
||||
$Parameters
|
||||
);
|
||||
|
||||
db::run("UPDATE assets SET LastServerUpdate = UNIX_TIMESTAMP() WHERE id = :PlaceID", [":PlaceID" => $JobInfo->PlaceID]);
|
||||
|
||||
if ($JobInfo->Status == $Status) die("OK");
|
||||
|
||||
if ($Status == "Loading")
|
||||
{
|
||||
// refresh the gameserver's job count
|
||||
Games::RefreshJobCount($JobInfo->ServerID);
|
||||
}
|
||||
else if ($Status == "Ready")
|
||||
{
|
||||
// mark place as having a running game
|
||||
db::run("UPDATE assets SET ServerRunning = 1 WHERE id = :PlaceID", [":PlaceID" => $JobInfo->PlaceID]);
|
||||
}
|
||||
else if ($Status == "Closed" || $Status == "Crashed")
|
||||
{
|
||||
// refresh the gameserver's job count
|
||||
Games::RefreshJobCount($JobInfo->ServerID);
|
||||
|
||||
// close all player sessions
|
||||
db::run("UPDATE GameJobs SET PlayerCount = 0 WHERE JobID = :JobID", [":JobID" => $JobID]);
|
||||
db::run("UPDATE GameJobSessions SET Active = 0 WHERE JobID = :JobID", [":JobID" => $JobID]);
|
||||
|
||||
// refresh the game's active players
|
||||
Games::RefreshActivePlayers($JobInfo->PlaceID);
|
||||
|
||||
// refresh running game marker for place
|
||||
Games::RefreshRunningGameMarker($JobInfo->PlaceID);
|
||||
}
|
||||
|
||||
echo "OK";
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php require $_SERVER['DOCUMENT_ROOT']."/api/private/core.php";
|
||||
if (!Polygon::IsGameserverAuthorized()) PageBuilder::errorCode(404);
|
||||
|
||||
header("Pragma: no-cache");
|
||||
header("Cache-Control: no-cache");
|
||||
header("content-type: text/plain; charset=utf-8");
|
||||
|
||||
$Username = api::GetParameter("GET", "Username", "string");
|
||||
$UserID = api::GetParameter("GET", "UserID", "string");
|
||||
$Ticket = api::GetParameter("GET", "Ticket", "string");
|
||||
$JobID = api::GetParameter("GET", "JobID", "string");
|
||||
|
||||
$TicketInfo = db::run(
|
||||
"SELECT GameJobSessions.*, users.username, users.adminlevel FROM GameJobSessions
|
||||
INNER JOIN users ON users.id = UserID WHERE SecurityTicket = :Ticket",
|
||||
[":Ticket" => $Ticket]
|
||||
)->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
if ($TicketInfo === false) die("False");
|
||||
if ($TicketInfo->Verified) die("False");
|
||||
if ($TicketInfo->UserID != $UserID) die("False");
|
||||
if ($TicketInfo->username != $Username) die("False");
|
||||
if ($TicketInfo->JobID != $JobID) die("False");
|
||||
|
||||
db::run("UPDATE GameJobSessions SET Verified = 1 WHERE SecurityTicket = :Ticket", [":Ticket" => $Ticket]);
|
||||
|
||||
die("True");
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
class Auth
|
||||
{
|
||||
// i wonder if its worth putting the plain password only in the constructor
|
||||
// for the sake of efficiency - usually it works out well, however in the
|
||||
// change password api you end up having to instantiate two auth objects
|
||||
// oh well
|
||||
// by the way, this is like the only OOP thing in the entirety of polygon
|
||||
// (apart from third party libaries). maybe i should change that. todo?
|
||||
|
||||
private $plaintext = "";
|
||||
private $key = "";
|
||||
|
||||
function CreatePassword()
|
||||
{
|
||||
return \ParagonIE\PasswordLock\PasswordLock::hashAndEncrypt($this->plaintext, $this->key);
|
||||
}
|
||||
|
||||
function VerifyPassword($storedtext)
|
||||
{
|
||||
if(strpos($storedtext, "$2y$10") !== false) //standard bcrypt - used since 04/09/2020
|
||||
return password_verify($this->plaintext, $storedtext);
|
||||
elseif(strpos($storedtext, "def50200") !== false) //argon2id w/ encryption - used since 26/02/2021
|
||||
return \ParagonIE\PasswordLock\PasswordLock::decryptAndVerify($this->plaintext, $storedtext, $this->key);
|
||||
}
|
||||
|
||||
function UpdatePassword($userId)
|
||||
{
|
||||
$pwhash = $this->createPassword();
|
||||
db::run("UPDATE users SET password = :hash, lastpwdchange = UNIX_TIMESTAMP() WHERE id = :id", [":hash" => $pwhash, ":id" => $userId]);
|
||||
}
|
||||
|
||||
function __construct($plaintext)
|
||||
{
|
||||
if(!class_exists('Defuse\Crypto\Key')) Polygon::ImportLibrary("PasswordLock");
|
||||
$this->plaintext = $plaintext;
|
||||
$this->key = \Defuse\Crypto\Key::loadFromAsciiSafeString(SITE_CONFIG["keys"]["passwordEncryption"]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
<?php
|
||||
|
||||
class Catalog
|
||||
{
|
||||
public static array $types =
|
||||
[
|
||||
1 => "Image", // (internal use only - this is used for asset images)
|
||||
2 => "T-Shirt",
|
||||
3 => "Audio",
|
||||
4 => "Mesh", // (internal use only)
|
||||
5 => "Lua", // (internal use only - use this for corescripts and linkedtool scripts)
|
||||
6 => "HTML", // (deprecated - dont use)
|
||||
7 => "Text", // (deprecated - dont use)
|
||||
8 => "Hat",
|
||||
9 => "Place", // (unused as of now)
|
||||
10 => "Model",
|
||||
11 => "Shirt",
|
||||
12 => "Pants",
|
||||
13 => "Decal",
|
||||
16 => "Avatar", // (deprecated - dont use)
|
||||
17 => "Head",
|
||||
18 => "Face",
|
||||
19 => "Gear",
|
||||
21 => "Badge", // (unused as of now)
|
||||
22 => "Group Emblem", // (internal use only - these are basically just images really)
|
||||
24 => "Animation",
|
||||
25 => "Arms",
|
||||
26 => "Legs",
|
||||
27 => "Torso",
|
||||
28 => "Right Arm",
|
||||
29 => "Left Arm",
|
||||
30 => "Left Leg",
|
||||
31 => "Right Leg",
|
||||
32 => "Package",
|
||||
33 => "YoutubeVideo",
|
||||
34 => "Gamepass",
|
||||
35 => "App",
|
||||
37 => "Code",
|
||||
38 => "Plugin", // (ignore everything beyond this point)
|
||||
39 => "SolidModel",
|
||||
40 => "MeshPart",
|
||||
41 => "Hair Accessory",
|
||||
42 => "Face Accessory",
|
||||
43 => "Neck Accessory",
|
||||
44 => "Shoulder Accessory",
|
||||
45 => "Front Accessory",
|
||||
46 => "Back Accessory",
|
||||
47 => "Waist Accessory",
|
||||
48 => "Climb Animation",
|
||||
49 => "Death Animation",
|
||||
50 => "Fall Animation",
|
||||
51 => "Idle Animation",
|
||||
52 => "Jump Animation",
|
||||
53 => "Run Animation",
|
||||
54 => "Swim Animation",
|
||||
55 => "Walk Animation",
|
||||
56 => "Pose Animation",
|
||||
59 => "LocalizationTableManifest",
|
||||
60 => "LocalizationTableTranslation",
|
||||
61 => "Emote Animation",
|
||||
62 => "Video",
|
||||
63 => "TexturePack",
|
||||
64 => "T-Shirt Accessory",
|
||||
65 => "Shirt Accessory",
|
||||
66 => "Pants Accessory",
|
||||
67 => "Jacket Accessory",
|
||||
68 => "Sweater Accessory",
|
||||
69 => "Shorts Accessory",
|
||||
70 => "Left Shoe Accessory",
|
||||
71 => "Right Shoe Accessory",
|
||||
72 => "Dress Skirt Accessory",
|
||||
73 => "Font Family",
|
||||
74 => "Font Face",
|
||||
75 => "MeshHiddenSurfaceRemoval"
|
||||
];
|
||||
|
||||
static function GetTypeByNum($type)
|
||||
{
|
||||
return self::$types[$type] ?? false;
|
||||
}
|
||||
|
||||
public static array $GearAttributesDisplay =
|
||||
[
|
||||
"melee" => ["text_sel" => "Melee", "text_item" => "Melee Weapon", "icon" => "far fa-sword"],
|
||||
"powerup" => ["text_sel" => "Power ups", "text_item" => "Power Up", "icon" => "far fa-arrow-alt-up"],
|
||||
"ranged" => ["text_sel" => "Ranged", "text_item" => "Ranged Weapon", "icon" => "far fa-bow-arrow"],
|
||||
"navigation" => ["text_sel" => "Navigation", "text_item" => "Navigation", "icon" => "far fa-compass"],
|
||||
"explosive" => ["text_sel" => "Explosives", "text_item" => "Explosive", "icon" => "far fa-bomb"],
|
||||
"musical" => ["text_sel" => "Musical", "text_item" => "Musical", "icon" => "far fa-music"],
|
||||
"social" => ["text_sel" => "Social", "text_item" => "Social Item", "icon" => "far fa-laugh"],
|
||||
"transport" => ["text_sel" => "Transport", "text_item" => "Personal Transport", "icon" => "far fa-motorcycle"],
|
||||
"building" => ["text_sel" => "Building", "text_item" => "Building", "icon" => "far fa-hammer"]
|
||||
];
|
||||
|
||||
public static array $GearAttributes =
|
||||
[
|
||||
"melee" => false,
|
||||
"powerup" => false,
|
||||
"ranged" => false,
|
||||
"navigation" => false,
|
||||
"explosive" => false,
|
||||
"musical" => false,
|
||||
"social" => false,
|
||||
"transport" => false,
|
||||
"building" => false
|
||||
];
|
||||
|
||||
static function ParseGearAttributes()
|
||||
{
|
||||
$gears = self::$GearAttributes;
|
||||
foreach($gears as $gear => $enabled) $gears[$gear] = isset($_POST["gear_$gear"]) && $_POST["gear_$gear"] == "on";
|
||||
self::$GearAttributes = $gears;
|
||||
}
|
||||
|
||||
static function GetAssetInfo($id)
|
||||
{
|
||||
return db::run(
|
||||
"SELECT assets.*, users.username, users.jointime,
|
||||
(SELECT COUNT(*) FROM ownedAssets WHERE assetId = assets.id AND userId != assets.creator) AS sales_total,
|
||||
(SELECT COUNT(*) FROM ownedAssets WHERE assetId = assets.id AND userId != assets.creator AND timestamp > :sda) AS sales_week
|
||||
FROM assets INNER JOIN users ON creator = users.id WHERE assets.id = :id",
|
||||
[":sda" => strtotime('7 days ago', time()), ":id" => $id])->fetch(PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
static function CreateAsset($options)
|
||||
{
|
||||
global $pdo;
|
||||
$columns = array_keys($options);
|
||||
|
||||
$querystring = "INSERT INTO assets (".implode(", ", $columns).", created, updated) ";
|
||||
array_walk($columns, function(&$value, $_){ $value = ":$value"; });
|
||||
$querystring .= "VALUES (".implode(", ", $columns).", UNIX_TIMESTAMP(), UNIX_TIMESTAMP())";
|
||||
|
||||
$query = $pdo->prepare($querystring);
|
||||
foreach($options as $option => $val) $query->bindParam(":$option", $options[$option], is_numeric($val) ? PDO::PARAM_INT : PDO::PARAM_STR);
|
||||
$query->execute();
|
||||
|
||||
$aid = $pdo->lastInsertId();
|
||||
$uid = $options["creator"] ?? SESSION["user"]["id"];
|
||||
|
||||
db::run("INSERT INTO ownedAssets (assetId, userId, timestamp) VALUES (:aid, :uid, UNIX_TIMESTAMP())", [":aid" => $aid, ":uid" => $uid]);
|
||||
|
||||
return $aid;
|
||||
}
|
||||
|
||||
static function DeleteAsset($AssetID)
|
||||
{
|
||||
$Location = SITE_CONFIG["paths"]["assets"].$AssetID;
|
||||
if (file_exists($Location)) unlink($Location);
|
||||
}
|
||||
|
||||
static function OwnsAsset($uid, $aid)
|
||||
{
|
||||
return db::run("SELECT COUNT(*) FROM ownedAssets WHERE assetId = :aid AND userId = :uid", [":aid" => $aid, ":uid" => $uid])->fetchColumn();
|
||||
}
|
||||
|
||||
static function GenerateGraphicXML($type, $assetID)
|
||||
{
|
||||
$strings =
|
||||
[
|
||||
"T-Shirt" => ["class" => "ShirtGraphic", "contentName" => "Graphic", "stringName" => "Shirt Graphic"],
|
||||
"Decal" => ["class" => "Decal", "contentName" => "Texture", "stringName" => "Decal"],
|
||||
"Face" => ["class" => "Decal", "contentName" => "Texture", "stringName" => "face"],
|
||||
"Shirt" => ["class" => "Shirt", "contentName" => "ShirtTemplate", "stringName" => "Shirt"],
|
||||
"Pants" => ["class" => "Pants", "contentName" => "PantsTemplate", "stringName" => "Pants"]
|
||||
];
|
||||
ob_start(); ?>
|
||||
<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" version="4">
|
||||
<External>null</External>
|
||||
<External>nil</External>
|
||||
<Item class="<?=$strings[$type]["class"]?>" referent="RBX0">
|
||||
<Properties>
|
||||
<?php if($type == "Decal" || $type == "Face") { ?>
|
||||
<token name="Face">5</token>
|
||||
<string name="Name"><?=$strings[$type]["stringName"]?></string>
|
||||
<float name="Shiny">20</float>
|
||||
<float name="Specular">0</float>
|
||||
<Content name="Texture">
|
||||
<url>%ASSETURL%<?=$assetID?></url>
|
||||
</Content>
|
||||
<?php } else { ?>
|
||||
<Content name="<?=$strings[$type]["contentName"]?>">
|
||||
<url>%ASSETURL%<?=$assetID?></url>
|
||||
</Content>
|
||||
<string name="Name"><?=$strings[$type]["stringName"]?></string>
|
||||
<?php } ?>
|
||||
<bool name="archivable">true</bool>
|
||||
</Properties>
|
||||
</Item>
|
||||
</roblox>
|
||||
<?php return ob_get_clean();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
class Discord
|
||||
{
|
||||
const WEBHOOK_POLYGON_GITPULL = "https://discord.com/api/webhooks/";
|
||||
const WEBHOOK_POLYGON_JOINLOG = "https://discord.com/api/webhooks/";
|
||||
const WEBHOOK_POLYGON_ERRORLOG = "https://discord.com/api/webhooks/";
|
||||
const WEBHOOK_KUSH = "https://discord.com/api/webhooks/";
|
||||
|
||||
static function IsVerified($UserID)
|
||||
{
|
||||
$IsVerified = db::run("SELECT discordID FROM users WHERE id = :UserID", [":UserID" => $UserID])->fetchColumn();
|
||||
if ($IsVerified === NULL) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static function GetUserInfo($UserID)
|
||||
{
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch,
|
||||
[
|
||||
CURLOPT_URL => "https://discord.com/api/v8/users/$UserID",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => ["Authorization: Bot"]
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if ($httpcode != 200) return false;
|
||||
|
||||
$response = json_decode($response);
|
||||
if ($response == NULL) return false;
|
||||
|
||||
return (object)
|
||||
[
|
||||
"username" => $response->username,
|
||||
"tag" => $response->discriminator,
|
||||
"id" => $response->id,
|
||||
"avatar" => "https://cdn.discordapp.com/avatars/{$response->id}/{$response->avatar}.png",
|
||||
"color" => $response->accent_color,
|
||||
"banner" => $response->banner,
|
||||
"banner_color" => $response->banner_color
|
||||
];
|
||||
}
|
||||
|
||||
static function SendToWebhook($Payload, $Webhook, $EscapeContent = true)
|
||||
{
|
||||
// example payload:
|
||||
// $payload = ["username" => "test", "content" => "test", "avatar_url" => "https://polygon.pizzaboxer.xyz/thumbs/avatar?id=1&x=100&y=100"];
|
||||
|
||||
if($EscapeContent)
|
||||
{
|
||||
$Payload["content"] = str_ireplace(["\\", "`"], ["\\\\", "\\`"], $Payload["content"]);
|
||||
$Payload["content"] = str_ireplace(["@everyone", "@here"], ["`@everyone`", "`@here`"], $Payload["content"]);
|
||||
$Payload["content"] = preg_replace("/(<@[0-9]+>)/i", "`$1`", $Payload["content"]);
|
||||
}
|
||||
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, $Webhook);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(['payload_json' => json_encode($Payload)]));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
Polygon::ImportClass("Discord");
|
||||
|
||||
class ErrorHandler
|
||||
{
|
||||
private $Exception;
|
||||
private $Type;
|
||||
private $String;
|
||||
private $File;
|
||||
private $Line;
|
||||
|
||||
private function GetType($Type)
|
||||
{
|
||||
switch ($Type)
|
||||
{
|
||||
case E_ERROR: case E_USER_ERROR: return "Fatal error";
|
||||
case E_WARNING: case E_USER_WARNING: return "Warning";
|
||||
case E_NOTICE: case E_USER_NOTICE: return "Notice";
|
||||
case E_DEPRECATED: case E_USER_DEPRECATED: return "Deprecated";
|
||||
default: return "Unknown error type $Type";
|
||||
}
|
||||
}
|
||||
|
||||
private function GetVerboseMessage()
|
||||
{
|
||||
$VerboseMessage = "";
|
||||
|
||||
if ($this->Type == "Exception")
|
||||
{
|
||||
$VerboseMessage .= sprintf("Fatal Error: Uncaught Exception: %s in %s:%d\n", $this->Exception->getMessage(), $this->Exception->getFile(), $this->Exception->getLine());
|
||||
$VerboseMessage .= "Stack trace:\n";
|
||||
$VerboseMessage .= sprintf("%s\n", $this->Exception->getTraceAsString());
|
||||
$VerboseMessage .= sprintf(" thrown in %s on line %d", $this->Exception->getFile(), $this->Exception->getLine());
|
||||
}
|
||||
else
|
||||
{
|
||||
$VerboseMessage .= sprintf("%s: %s in %s on line %s", $this->Type, $this->String, $this->File, $this->Line);
|
||||
}
|
||||
|
||||
return $VerboseMessage;
|
||||
}
|
||||
|
||||
private function WriteLog()
|
||||
{
|
||||
$LogFile = $_SERVER['DOCUMENT_ROOT']."/api/private/ErrorLog.json";
|
||||
$LogID = generateUUID();
|
||||
|
||||
if (!file_exists($LogFile)) file_put_contents($LogFile, "[]");
|
||||
|
||||
$Log = json_decode(file_get_contents($LogFile), true);
|
||||
$Message = $this->GetVerboseMessage();
|
||||
$Parameters = $_SERVER["REQUEST_URI"];
|
||||
|
||||
$Log[$LogID] =
|
||||
[
|
||||
"Timestamp" => time(),
|
||||
// "GETParameters" => $_GET,
|
||||
"GETParameters" => $Parameters,
|
||||
"Message" => $Message
|
||||
];
|
||||
|
||||
file_put_contents($LogFile, json_encode($Log));
|
||||
|
||||
Discord::SendToWebhook(["content" => "<@194171603049775113> An unexpected error occurred\nError ID: `{$LogID}`\nTime: `".date('d/m/Y h:i:s A')."`\nParameters: `$Parameters`\nMessage:\n```$Message```"], Discord::WEBHOOK_POLYGON_ERRORLOG, false);
|
||||
|
||||
return $LogID;
|
||||
}
|
||||
|
||||
private function LogAndRedirect()
|
||||
{
|
||||
$LogID = $this->WriteLog();
|
||||
|
||||
if (headers_sent())
|
||||
{
|
||||
die("An unexpected error occurred! More info: $LogID");
|
||||
}
|
||||
else if (defined("SESSION") && isset(SESSION["user"]["adminlevel"]) && SESSION["user"]["adminlevel"] != 0)
|
||||
{
|
||||
redirect("/error?id=$LogID&verbose=true");
|
||||
}
|
||||
else
|
||||
{
|
||||
redirect("/error?id=$LogID");
|
||||
}
|
||||
}
|
||||
|
||||
public function HandleError($Type, $String, $File, $Line)
|
||||
{
|
||||
$this->Type = $this->GetType($Type);
|
||||
$this->String = $String;
|
||||
$this->File = $File;
|
||||
$this->Line = $Line;
|
||||
|
||||
$this->LogAndRedirect();
|
||||
}
|
||||
|
||||
public function HandleException($Exception)
|
||||
{
|
||||
$this->Type = "Exception";
|
||||
$this->Exception = $Exception;
|
||||
|
||||
$this->LogAndRedirect();
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
set_error_handler([$this, "HandleError"]);
|
||||
set_exception_handler([$this, "HandleException"]);
|
||||
}
|
||||
|
||||
public static function GetLog($LogID = false)
|
||||
{
|
||||
$LogFile = $_SERVER['DOCUMENT_ROOT']."/api/private/ErrorLog.json";
|
||||
if (!file_exists($LogFile)) file_put_contents($LogFile, "[]");
|
||||
|
||||
$Log = json_decode(file_get_contents($LogFile), true);
|
||||
|
||||
if ($LogID !== false) return $Log[$LogID] ?? false;
|
||||
return $Log;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
class Forum
|
||||
{
|
||||
static function GetThreadInfo($id)
|
||||
{
|
||||
return db::run("SELECT * FROM forum_threads WHERE id = :id", [":id" => $id])->fetch(PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
static function GetReplyInfo($id)
|
||||
{
|
||||
return db::run("SELECT * FROM forum_replies WHERE id = :id", [":id" => $id])->fetch(PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
static function GetThreadReplies($id)
|
||||
{
|
||||
return db::run("SELECT COUNT(*) FROM forum_replies WHERE threadId = :id AND NOT deleted", [":id" => $id])->fetchColumn() ?: "-";
|
||||
}
|
||||
|
||||
static function GetSubforumInfo($id)
|
||||
{
|
||||
return db::run("SELECT * FROM forum_subforums WHERE id = :id", [":id" => $id])->fetch(PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
static function GetSubforumThreadCount($id, $includeReplies = false)
|
||||
{
|
||||
$threads = db::run("SELECT COUNT(*) FROM forum_threads WHERE subforumid = :id", [":id" => $id])->fetchColumn();
|
||||
if(!$includeReplies) return $threads ?: '-';
|
||||
|
||||
$replies = db::run("SELECT COUNT(*) from forum_replies WHERE threadId IN (SELECT id FROM forum_threads WHERE subforumid = :id)", [":id" => $id])->fetchColumn();
|
||||
$total = $threads + $replies;
|
||||
|
||||
return $total ?: '-';
|
||||
}
|
||||
}
|
||||
|
||||
class pagination
|
||||
{
|
||||
// this is ugly and sucks
|
||||
// really this is only for the forums
|
||||
// everything else uses standard next and back pagination
|
||||
|
||||
public static int $page = 1;
|
||||
public static int $pages = 1;
|
||||
public static string $url = '/';
|
||||
public static array $pager = [1 => 1, 2 => 1, 3 => 1];
|
||||
|
||||
public static function initialize()
|
||||
{
|
||||
self::$pager[1] = self::$page-1; self::$pager[2] = self::$page; self::$pager[3] = self::$page+1;
|
||||
|
||||
if(self::$page <= 2){ self::$pager[1] = self::$page; self::$pager[2] = self::$page+1; self::$pager[3] = self::$page+2; }
|
||||
if(self::$page == 1){ self::$pager[1] = self::$page+1; }
|
||||
|
||||
if(self::$page >= self::$pages-1){ self::$pager[1] = self::$pages-3; self::$pager[2] = self::$pages-2; self::$pager[3] = self::$pages-1; }
|
||||
if(self::$page == self::$pages){ self::$pager[1] = self::$pages-1; self::$pager[2] = self::$pages-2; }
|
||||
if(self::$page == self::$pages-1){ self::$pager[1] = self::$pages-2; self::$pager[2] = self::$pages-1; }
|
||||
}
|
||||
|
||||
public static function insert()
|
||||
{
|
||||
if(self::$pages <= 1) return;
|
||||
?>
|
||||
<nav>
|
||||
<ul class="pagination justify-content-end mb-0">
|
||||
<li class="page-item<?=self::$page<=1?' disabled':''?>">
|
||||
<a class="page-link" <?=self::$page>1?'href="'.self::$url.(self::$page-1).'"':''?>aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
<span class="sr-only">Previous</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item<?=self::$page==1?' active':''?>"><a class="page-link"<?=self::$page!=1?' href="'.self::$url.'1" ':''?>>1</a></li>
|
||||
<?php if(self::$pages > 2){ if(self::$page > 3){ ?><li class="page-item disabled"><a class="page-link" href="#" tabindex="-1">…</a></li><?php } ?>
|
||||
<?php for($i=1; $i<4; $i++){ if(self::$page == $i-1 || self::$pages-self::$page == $i-2) break; ?>
|
||||
<li class="page-item<?=self::$page==self::$pager[$i]?' active':''?>"><a class="page-link"<?=self::$page!=self::$pager[$i]?' href="'.self::$url.self::$pager[$i].'" ':''?>><?=number_format(self::$pager[$i])?></a></li>
|
||||
<?php } ?>
|
||||
<?php if(self::$page < self::$pages-2){ ?><li class="page-item disabled"><a class="page-link" href="#" tabindex="-1">…</a></li><?php } } ?>
|
||||
<li class="page-item<?=self::$page==self::$pages?' active':''?>"><a class="page-link"<?=self::$page!=self::$pages?' href="'.self::$url.self::$pages.'" ':''?>><?=number_format(self::$pages)?></a></li>
|
||||
<li class="page-item<?=self::$page>self::$pages?' disabled':''?>">
|
||||
<a class="page-link" <?=self::$page<self::$pages?'href="'.self::$url.(self::$page+1).'"':''?>aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
<span class="sr-only">Previous</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
class Games
|
||||
{
|
||||
static function GetServerInfo($id, $UserID = 0, $CheckIfWhitelisted = false)
|
||||
{
|
||||
if ($CheckIfWhitelisted)
|
||||
{
|
||||
return db::run(
|
||||
"SELECT selfhosted_servers.*,
|
||||
users.username, users.jointime,
|
||||
(SELECT COUNT(DISTINCT uid) FROM client_sessions WHERE ping+35 > UNIX_TIMESTAMP() AND serverID = :id AND valid AND verified) AS players,
|
||||
(ping+35 > UNIX_TIMESTAMP()) AS online
|
||||
FROM selfhosted_servers
|
||||
INNER JOIN users ON users.id = hoster
|
||||
WHERE selfhosted_servers.id = :id AND (Privacy = \"Public\" OR hoster = :UserID OR JSON_CONTAINS(PrivacyWhitelist, :UserID, \"$\"))",
|
||||
[":id" => $id, ":UserID" => $UserID]
|
||||
)->fetch(PDO::FETCH_OBJ);
|
||||
}
|
||||
else
|
||||
{
|
||||
return db::run(
|
||||
"SELECT selfhosted_servers.*,
|
||||
users.username, users.jointime,
|
||||
(SELECT COUNT(DISTINCT uid) FROM client_sessions WHERE ping+35 > UNIX_TIMESTAMP() AND serverID = :id AND valid AND verified) AS players,
|
||||
(ping+35 > UNIX_TIMESTAMP()) AS online
|
||||
FROM selfhosted_servers
|
||||
INNER JOIN users ON users.id = hoster WHERE selfhosted_servers.id = :id",
|
||||
[":id" => $id]
|
||||
)->fetch(PDO::FETCH_OBJ);
|
||||
}
|
||||
}
|
||||
|
||||
static function GetPlayersInServer($serverID)
|
||||
{
|
||||
return db::run("
|
||||
SELECT users.* FROM selfhosted_servers
|
||||
INNER JOIN client_sessions ON client_sessions.ping+35 > UNIX_TIMESTAMP() AND serverID = selfhosted_servers.id AND valid
|
||||
INNER JOIN users ON users.id = uid
|
||||
WHERE selfhosted_servers.id = :id GROUP BY client_sessions.uid", [":id" => $serverID]);
|
||||
}
|
||||
|
||||
static function GetPlayerCountInServer($ServerID)
|
||||
{
|
||||
$PlayerCount = db::run(
|
||||
"SELECT COUNT(DISTINCT uid) FROM client_sessions WHERE ping+35 > UNIX_TIMESTAMP() AND serverID = :ServerID AND valid AND verified)",
|
||||
[":ServerID" => $ServerID]
|
||||
)->fetchColumn();
|
||||
|
||||
return (int) $PlayerCount;
|
||||
}
|
||||
|
||||
static function GetPlayersInGame($JobID)
|
||||
{
|
||||
$Players = db::run(
|
||||
"SELECT GameJobSessions.UserID, users.username AS Username FROM GameJobSessions
|
||||
INNER JOIN users ON users.id = UserID
|
||||
WHERE JobID = :JobID AND Active",
|
||||
[":JobID" => $JobID]
|
||||
)->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($Players as &$Player)
|
||||
{
|
||||
$Player["Thumbnail"] = Thumbnails::GetAvatar($Player["UserID"]);
|
||||
}
|
||||
|
||||
return $Players;
|
||||
}
|
||||
|
||||
static function CheckIfPlayerInGame($UserID, $JobID)
|
||||
{
|
||||
return db::run(
|
||||
"SELECT COUNT(*) FROM GameJobSessions WHERE Active AND Verified AND UserID = :UserID AND JobID = :JobID",
|
||||
[":UserID" => $UserID, ":JobID" => $JobID]
|
||||
)->fetchColumn();
|
||||
}
|
||||
|
||||
static function GetJobSession($Ticket)
|
||||
{
|
||||
// used for clientpresence and placevisit
|
||||
// we just want to make sure that the ticket exists and the game job is open
|
||||
return db::run(
|
||||
"SELECT GameJobSessions.*, GameJobs.PlaceID FROM GameJobSessions
|
||||
INNER JOIN GameJobs ON GameJobs.JobID = GameJobSessions.JobID
|
||||
WHERE SecurityTicket = :Ticket AND Status = \"Ready\"",
|
||||
[":Ticket" => $Ticket]
|
||||
)->fetch(PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
static function GetJobInfo($JobID)
|
||||
{
|
||||
return db::run(
|
||||
"SELECT * FROM GameJobs WHERE JobID = :JobID",
|
||||
[":JobID" => $JobID]
|
||||
)->fetch(PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
static function RefreshJobCount($ServerID)
|
||||
{
|
||||
$JobCount = db::run(
|
||||
"SELECT COUNT(*) FROM GameJobs WHERE ServerID = :ServerID AND Status IN (\"Loading\", \"Ready\")",
|
||||
[":ServerID" => $ServerID]
|
||||
)->fetchColumn();
|
||||
|
||||
db::run(
|
||||
"UPDATE GameServers SET ActiveJobs = :JobCount WHERE ServerID = :ServerID",
|
||||
[":JobCount" => $JobCount, ":ServerID" => $ServerID]
|
||||
);
|
||||
}
|
||||
|
||||
static function RefreshActivePlayers($PlaceID)
|
||||
{
|
||||
$ActivePlayers = db::run(
|
||||
"SELECT COUNT(*) FROM GameJobSessions WHERE Active AND JobID IN
|
||||
(
|
||||
SELECT JobID FROM GameJobs WHERE PlaceID = :PlaceID AND Status = \"Ready\"
|
||||
)",
|
||||
[":PlaceID" => $PlaceID]
|
||||
)->fetchColumn();
|
||||
|
||||
db::run(
|
||||
"UPDATE assets SET ActivePlayers = :ActivePlayers WHERE id = :PlaceID",
|
||||
[":ActivePlayers" => $ActivePlayers, ":PlaceID" => $PlaceID]
|
||||
);
|
||||
}
|
||||
|
||||
static function RefreshRunningGameMarker($PlaceID)
|
||||
{
|
||||
db::run(
|
||||
"UPDATE assets SET ServerRunning =
|
||||
(
|
||||
SELECT COUNT(*) FROM GameJobs WHERE PlaceID = :PlaceID AND Status = \"Ready\"
|
||||
) > 0 WHERE id = :PlaceID",
|
||||
[":PlaceID" => $PlaceID]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
<?php
|
||||
|
||||
class Groups
|
||||
{
|
||||
static function GetGroupInfo($GroupID, $Barebones = false, $Force = false)
|
||||
{
|
||||
if($Barebones)
|
||||
{
|
||||
$GroupInfo = db::run("SELECT * FROM groups WHERE groups.id = :id", [":id" => $GroupID])->fetch(PDO::FETCH_OBJ);
|
||||
}
|
||||
else
|
||||
{
|
||||
$GroupInfo = db::run(
|
||||
"SELECT groups.*,
|
||||
(SELECT COUNT(*) FROM groups_members WHERE GroupID = :id AND NOT Pending) AS MemberCount,
|
||||
users.username AS ownername FROM groups
|
||||
INNER JOIN users ON users.id = groups.owner
|
||||
WHERE groups.id = :id",
|
||||
[":id" => $GroupID]
|
||||
)->fetch(PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
if(!$Force && $GroupInfo && $GroupInfo->deleted) return false;
|
||||
return $GroupInfo;
|
||||
}
|
||||
|
||||
static function GetGroupStatus($GroupID)
|
||||
{
|
||||
return db::run(
|
||||
"SELECT feed.*, users.username FROM feed
|
||||
INNER JOIN users ON users.id = feed.userId
|
||||
WHERE groupId = :GroupID ORDER BY id DESC LIMIT 1",
|
||||
[":GroupID" => $GroupID]
|
||||
)->fetch(PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
static function GetLastGroupUserJoined($UserID)
|
||||
{
|
||||
$GroupID = db::run(
|
||||
"SELECT GroupID FROM groups_members WHERE UserID = :UserID ORDER BY Joined DESC LIMIT 1",
|
||||
[":UserID" => $UserID]
|
||||
)->fetchColumn();
|
||||
|
||||
return self::GetGroupInfo($GroupID);
|
||||
}
|
||||
|
||||
static function GetRankInfo($GroupID, $RankLevel)
|
||||
{
|
||||
$RankInfo = db::run(
|
||||
"SELECT * FROM groups_ranks WHERE GroupID = :GroupID AND Rank = :RankLevel",
|
||||
[":GroupID" => $GroupID, ":RankLevel" => $RankLevel]
|
||||
)->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
if(!$RankInfo) return false;
|
||||
|
||||
return (object) [
|
||||
"Name" => $RankInfo->Name,
|
||||
"Description" => $RankInfo->Description,
|
||||
"Level" => $RankInfo->Rank,
|
||||
"Permissions" => json_decode($RankInfo->Permissions)
|
||||
];
|
||||
}
|
||||
|
||||
static function GetGroupRanks($GroupID, $includeGuest = false)
|
||||
{
|
||||
if($includeGuest)
|
||||
return db::run("SELECT * FROM groups_ranks WHERE GroupID = :id ORDER BY Rank ASC", [":id" => $GroupID]);
|
||||
else
|
||||
return db::run("SELECT * FROM groups_ranks WHERE GroupID = :id AND Rank != 0 ORDER BY Rank ASC", [":id" => $GroupID]);
|
||||
}
|
||||
|
||||
static function CheckIfUserInGroup($UserID, $GroupID)
|
||||
{
|
||||
return db::run(
|
||||
"SELECT * FROM groups_members WHERE UserID = :UserID AND GroupID = :GroupID",
|
||||
[":UserID" => $UserID, ":GroupID" => $GroupID]
|
||||
)->rowCount();
|
||||
}
|
||||
|
||||
static function GetUserRank($UserID, $GroupID)
|
||||
{
|
||||
$RankLevel = db::run(
|
||||
"SELECT Rank FROM groups_members WHERE UserID = :UserID And GroupID = :GroupID",
|
||||
[":UserID" => $UserID, ":GroupID" => $GroupID]
|
||||
)->fetchColumn();
|
||||
|
||||
if(!$RankLevel) return self::GetRankInfo($GroupID, 0);
|
||||
|
||||
return self::GetRankInfo($GroupID, $RankLevel);
|
||||
}
|
||||
|
||||
static function GetUserGroups($UserID)
|
||||
{
|
||||
return db::run(
|
||||
"SELECT groups.* FROM groups_members
|
||||
INNER JOIN groups ON groups.id = groups_members.GroupID
|
||||
WHERE groups_members.UserID = :UserID
|
||||
ORDER BY groups_members.Joined DESC",
|
||||
[":UserID" => $UserID]
|
||||
);
|
||||
}
|
||||
|
||||
static function LogAction($GroupID, $Category, $Description)
|
||||
{
|
||||
// small note: when using this, you gotta be very careful about what you pass into the description
|
||||
// the description must be sanitized when inserted into the db, not when fetched from an api
|
||||
// this is because the description may contain hyperlinks or other html elements
|
||||
// also here's a list of categories:
|
||||
|
||||
// Delete Post
|
||||
// Remove Member
|
||||
// Accept Join Request
|
||||
// Decline Join Request
|
||||
// Post Shout
|
||||
// Change Rank
|
||||
// Buy Ad
|
||||
// Send Ally Request
|
||||
// Create Enemy
|
||||
// Accept Ally Request
|
||||
// Decline Ally Request
|
||||
// Delete Ally
|
||||
// Delete Enemy
|
||||
// Add Group Place
|
||||
// Delete Group Place
|
||||
// Create Items
|
||||
// Configure Items
|
||||
// Spend Group Funds
|
||||
// Change Owner
|
||||
// Delete
|
||||
// Adjust Currency Amounts
|
||||
// Abandon
|
||||
// Claim
|
||||
// Rename
|
||||
// Change Description
|
||||
// Create Group Asset
|
||||
// Update Group Asset
|
||||
// Configure Group Asset
|
||||
// Revert Group Asset
|
||||
// Create Group Developer Product
|
||||
// Configure Group Game
|
||||
// Lock
|
||||
// Unlock
|
||||
// Create Pass
|
||||
// Create Badge
|
||||
// Configure Badge
|
||||
// Save Place
|
||||
// Publish Place
|
||||
// Invite to Clan
|
||||
// Kick from Clan
|
||||
// Cancel Clan Invite
|
||||
// Buy Clan
|
||||
|
||||
if(!SESSION) return false;
|
||||
$MyRank = self::GetUserRank(SESSION["user"]["id"], $GroupID);
|
||||
|
||||
db::run(
|
||||
"INSERT INTO groups_audit (GroupID, Category, Time, UserID, Rank, Description)
|
||||
VALUES (:GroupID, :Category, UNIX_TIMESTAMP(), :UserID, :Rank, :Description)",
|
||||
[":GroupID" => $GroupID, ":Category" => $Category, ":UserID" => SESSION["user"]["id"], ":Rank" => $MyRank->Name, ":Description" => $Description]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
class Gzip
|
||||
{
|
||||
// this is to compress models and places to help conserve space
|
||||
// this should be used only for models and places, nothing else
|
||||
|
||||
//http://stackoverflow.com/questions/6073397/how-do-you-create-a-gz-file-using-php
|
||||
static function Compress(string $inFilename, int $level = 9): string
|
||||
{
|
||||
// Is the file gzipped already?
|
||||
$extension = pathinfo($inFilename, PATHINFO_EXTENSION);
|
||||
if ($extension == "gz") { return $inFilename; }
|
||||
|
||||
// Open input file
|
||||
$inFile = fopen($inFilename, "rb");
|
||||
if ($inFile === false) { throw new \Exception("Unable to open input file: $inFilename"); }
|
||||
|
||||
// Open output file
|
||||
$gzFilename = $inFilename.".gz";
|
||||
$gzFile = gzopen($gzFilename, "wb".$level);
|
||||
if ($gzFile === false)
|
||||
{
|
||||
fclose($inFile);
|
||||
throw new \Exception("Unable to open output file: $gzFilename");
|
||||
}
|
||||
|
||||
// Stream copy
|
||||
$length = 65536 * 1024; // 512 kB
|
||||
while (!feof($inFile)) { gzwrite($gzFile, fread($inFile, $length)); }
|
||||
|
||||
// Close files
|
||||
fclose($inFile);
|
||||
gzclose($gzFile);
|
||||
|
||||
// Return the new filename
|
||||
//delete original
|
||||
unlink($inFilename);
|
||||
rename($gzFilename, $inFilename);
|
||||
return $gzFilename;
|
||||
}
|
||||
|
||||
static function Decompress($filename, $buffer_size = 8192)
|
||||
{
|
||||
$buffer = "";
|
||||
$file = gzopen($filename, 'rb');
|
||||
while(!gzeof($file)) { $buffer .= gzread($file, $buffer_size); }
|
||||
gzclose($file);
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
static function IsGzEncoded($Data)
|
||||
{
|
||||
return mb_strpos($Data, "\x1f\x8b\x08") === 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
class Image
|
||||
{
|
||||
static function Process($handle, $options)
|
||||
{
|
||||
$image = $options["image"] ?? true;
|
||||
$resize = $options["resize"] ?? true;
|
||||
$keepRatio = $options["keepRatio"] ?? false;
|
||||
$scaleX = $options["scaleX"] ?? false;
|
||||
$scaleY = $options["scaleY"] ?? false;
|
||||
|
||||
$handle->file_new_name_ext = "";
|
||||
$handle->file_new_name_body = $options["name"];
|
||||
|
||||
if($image)
|
||||
{
|
||||
$handle->image_convert = "png";
|
||||
$handle->image_resize = $resize;
|
||||
if($resize)
|
||||
{
|
||||
if($keepRatio) $handle->image_ratio_fill = $options["align"];
|
||||
if($scaleX) $handle->image_ratio_x = true; else $handle->image_x = $options["x"];
|
||||
if($scaleY) $handle->image_ratio_y = true; else $handle->image_y = $options["y"];
|
||||
}
|
||||
}
|
||||
|
||||
$directory = Polygon::GetSharedResource($options["dir"]);
|
||||
|
||||
if(strlen($options["name"]) && file_exists($directory.$options["name"]))
|
||||
unlink($directory.$options["name"]);
|
||||
|
||||
$handle->process($directory);
|
||||
if(!$handle->processed) return $handle->error;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static function Resize($file, $w, $h, $path = false)
|
||||
{
|
||||
list($width, $height) = getimagesize($file);
|
||||
$src = imagecreatefrompng($file);
|
||||
$dst = imagecreatetruecolor($w, $h);
|
||||
imagealphablending($dst, false);
|
||||
imagesavealpha($dst, true);
|
||||
imagecopyresampled($dst, $src, 0, 0, 0, 0, $w, $h, $width, $height);
|
||||
|
||||
// this resize function is used in conjunction with an imagepng function
|
||||
// to resize an existing image and upload - having to do this eve
|
||||
if($path) imagepng($dst, $path);
|
||||
|
||||
return $dst;
|
||||
}
|
||||
|
||||
static function MergeLayers($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct)
|
||||
{
|
||||
$cut = imagecreatetruecolor($src_w, $src_h);
|
||||
imagecopy($cut, $dst_im, 0, 0, $dst_x, $dst_y, $src_w, $src_h);
|
||||
imagecopy($cut, $src_im, 0, 0, $src_x, $src_y, $src_w, $src_h);
|
||||
imagecopymerge($dst_im, $cut, $dst_x, $dst_y, 0, 0, $src_w, $src_h, $pct);
|
||||
}
|
||||
|
||||
// pre rendered thumbnails (scripts and audios) are all rendered with the same size
|
||||
// so this just sorta cleans up the whole thing
|
||||
static function RenderFromStaticImage($img, $assetID)
|
||||
{
|
||||
Image::Resize(SITE_CONFIG['paths']['thumbs']."/$img.png", 420, 420, SITE_CONFIG['paths']['thumbs_assets']."/$assetID-420x420.png");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
class Messages
|
||||
{
|
||||
static function getMessageInfoFromId($MessageId)
|
||||
{
|
||||
return db::run("SELECT * FROM messages WHERE ID = :msgId", [":msgId" => $MessageId])->fetch(PDO::FETCH_OBJ);
|
||||
}
|
||||
static function getAllSentMessages($userId)
|
||||
{
|
||||
return db::run("SELECT * FROM messages WHERE SenderID = :uId", [":uId" => $userId]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
class RBXClient
|
||||
{
|
||||
private static array $brickcolors =
|
||||
[
|
||||
"F2F3F3" => 1, "A1A5A2" => 2, "F9E999" => 3, "D7C59A" => 5, "C2DAB8" => 6, "E8BAC8" => 9, "80BBDC" => 11, "CB8442" => 12, "CC8E69" => 18, "C4281C" => 21, "C470A0" => 22, "0D69AC" => 23, "F5CD30" => 24, "624732" => 25, "1B2A35" => 26, "6D6E6C" => 27, "287F47" => 28, "A1C48C" => 29, "F3CF9B" => 36, "4B974B" => 37, "A05F35" => 38, "C1CADE" => 39, "ECECEC" => 40, "CD544B" => 41, "C1DFF0" => 42, "7BB6E8" => 43, "F7F18D" => 44, "B4D2E4" => 45, "D9856C" => 47, "84B68D" => 48, "F8F184" => 49, "ECE8DE" => 50, "EEC4B6" => 100, "DA867A" => 101, "6E99CA" => 102, "C7C1B7" => 103, "6B327C" => 104, "E29B40" => 105, "DA8541" => 106, "008F9C" => 107, "685C43" => 108, "435493" => 110, "BFB7B1" => 111, "6874AC" => 112, "E5ADC8" => 113, "C7D23C" => 115, "55A5AF" => 116, "B7D7D5" => 118, "A4BD47" => 119, "D9E4A7" => 120, "E7AC58" => 121, "D36F4C" => 123, "923978" => 124, "EAB892" => 125, "A5A5CB" => 126, "DCBC81" => 127, "AE7A59" => 128, "9CA3A8" => 131, "D5733D" => 133, "D8DD56" => 134, "74869D" => 135, "877C90" => 136, "E09864" => 137, "958A73" => 138, "203A56" => 140, "27462D" => 141, "CFE2F7" => 143, "7988A1" => 145, "958EA3" => 146, "938767" => 147, "575857" => 148, "161D32" => 149, "ABADAC" => 150, "789082" => 151, "957977" => 153, "7B2E2F" => 154, "FFF67B" => 157, "E1A4C2" => 158, "756C62" => 168, "97695B" => 176, "B48455" => 178, "898788" => 179, "D7A94B" => 180, "F9D62E" => 190, "E8AB2D" => 191, "694028" => 192, "CF6024" => 193, "A3A2A5" => 194, "4667A4" => 195, "23478B" => 196, "8E4285" => 198, "635F62" => 199, "828A5D" => 200, "E5E4DF" => 208, "B08E44" => 209, "709578" => 210, "79B5B5" => 211, "9FC3E9" => 212, "6C81B7" => 213, "904C2A" => 216, "7C5C46" => 217, "96709F" => 218, "6B629B" => 219, "A7A9CE" => 220, "CD6298" => 221, "E4ADC8" => 222, "DC9095" => 223, "F0D5A0" => 224, "EBB87F" => 225, "FDEA8D" => 226, "7DBBDD" => 232, "342B75" => 268, "506D54" => 301, "5B5D69" => 302, "0010B0" => 303, "2C651D" => 304, "527CAE" => 305, "335882" => 306, "102ADC" => 307, "3D1585" => 308, "348E40" => 309, "5B9A4C" => 310, "9FA1AC" => 311, "592259" => 312, "1F801D" => 313, "9FADC0" => 314, "0989CF" => 315, "7B007B" => 316, "7C9C6B" => 317, "8AAB85" => 318, "B9C4B1" => 319, "CACBD1" => 320, "A75E9B" => 321, "7B2F7B" => 322, "94BE81" => 323, "A8BD99" => 324, "DFDFDE" => 325, "970000" => 327, "B1E5A6" => 328, "98C2DB" => 329, "FF98DC" => 330, "FF5959" => 331, "750000" => 332, "EFB838" => 333, "F8D96D" => 334, "E7E7EC" => 335, "C7D4E4" => 336, "FF9494" => 337, "BE6862" => 338, "562424" => 339, "F1E7C7" => 340, "FEF3BB" => 341, "E0B2D0" => 342, "D490BD" => 343, "965555" => 344, "8F4C2A" => 345, "D3BE96" => 346, "E2DCBC" => 347, "EDEAEA" => 348, "E9DADA" => 349, "883E3E" => 350, "BC9B5D" => 351, "C7AC78" => 352, "CABFA3" => 353, "BBB3B2" => 354, "6C584B" => 355, "A0844F" => 356, "958988" => 357, "ABA89E" => 358, "AF9483" => 359, "966766" => 360, "564236" => 361, "7E683F" => 362, "69665C" => 363, "5A4C42" => 364, "6A3909" => 365, "F8F8F8" => 1001, "CDCDCD" => 1002, "111111" => 1003, "FF0000" => 1004, "FFB000" => 1005, "B480FF" => 1006, "A34B4B" => 1007, "C1BE42" => 1008, "FFFF00" => 1009, "0000FF" => 1010, "002060" => 1011, "2154B9" => 1012, "04AFEC" => 1013, "AA5500" => 1014, "AA00AA" => 1015, "FF66CC" => 1016, "FFAF00" => 1017, "12EED4" => 1018, "00FFFF" => 1019, "00FF00" => 1020, "3A7D15" => 1021, "7F8E64" => 1022, "8C5B9F" => 1023, "AFDDFF" => 1024, "FFC9C9" => 1025, "B1A7FF" => 1026, "9FF3E9" => 1027, "CCFFCC" => 1028, "FFFFCC" => 1029, "FFCC99" => 1030, "6225D1" => 1031, "FF00BF" => 1032
|
||||
];
|
||||
|
||||
static function CryptGetSignature($data)
|
||||
{
|
||||
$KeyLocation = sprintf("file://%s", Polygon::GetSharedResource("polygon_private.pem"));
|
||||
openssl_sign($data, $signature, openssl_pkey_get_private($KeyLocation));
|
||||
return base64_encode($signature);
|
||||
}
|
||||
|
||||
static function CryptSignScript($data, $assetID = false)
|
||||
{
|
||||
if($assetID) $data = "%{$assetID}%\n{$data}";
|
||||
else $data = "\n{$data}";
|
||||
$signedScript = "%" . self::CryptGetSignature($data) . "%{$data}";
|
||||
return $signedScript;
|
||||
}
|
||||
|
||||
static function HexToBrickColor($hex)
|
||||
{
|
||||
return self::$brickcolors[$hex] ?? false;
|
||||
}
|
||||
|
||||
static function BrickColorToHex($brickcolor)
|
||||
{
|
||||
return array_flip(self::$brickcolors)[$brickcolor] ?? false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
class System
|
||||
{
|
||||
static function GetFileSize($bytes, $binaryPrefix = true)
|
||||
{
|
||||
$unit=array('B','KB','MB','GB','TB','PB');
|
||||
if (!$bytes) return '0 ' . $unit[0];
|
||||
if ($binaryPrefix) return round($bytes/pow(1024,($i=floor(log($bytes,1024)))),2) .' '. ($unit[$i] ?? 'B');
|
||||
return round($bytes/pow(1000,($i=floor(log($bytes,1000)))),2) .' '. ($unit[$i] ?? 'B');
|
||||
}
|
||||
|
||||
static function GetFolderSize($path, $raw = false)
|
||||
{
|
||||
$io = popen("du -sb $path", "r");
|
||||
$size = (int)filter_var(explode($path, fgets($io, 4096), 2)[0], FILTER_SANITIZE_NUMBER_INT);
|
||||
pclose($io);
|
||||
|
||||
if($raw) return $size;
|
||||
return self::getFileSize($size);
|
||||
}
|
||||
|
||||
static function GetMemoryUsage()
|
||||
{
|
||||
$lines = explode("\n", file_get_contents('/proc/meminfo'));
|
||||
$total = (int) filter_var($lines[0], FILTER_SANITIZE_NUMBER_INT);
|
||||
$free = (int) filter_var($lines[1], FILTER_SANITIZE_NUMBER_INT);
|
||||
return (object)["total" => $total*1024, "free" => $free*1024];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
|
||||
class Thumbnails
|
||||
{
|
||||
// this is for use with the new polygon cdn
|
||||
|
||||
// currently this just calculates the sha1 hash
|
||||
// of the user's current thumbnail on the fly
|
||||
|
||||
// from what ive seen it doesnt affect performance
|
||||
// too much but ideally i would have the hash cached
|
||||
// in the database, but for now this should do fine
|
||||
|
||||
private static string $BaseURL = "https://polygoncdn.pizzaboxer.xyz/";
|
||||
|
||||
private static array $StatusThumbnails =
|
||||
[
|
||||
"pending-100x100.png" => "0180a01964362301c67cc47344ff34c2041573c0",
|
||||
"pending-110x110.png" => "e3dd8134956391d4b29070f3d4fc8db1a604f160",
|
||||
"pending-250x250.png" => "d2c46fc832fb48e1d24935893124d21f16cb5824",
|
||||
"pending-352x352.png" => "a4ce4cc7e648fba21da9093bcacf1c33c3903ab9",
|
||||
"pending-420x420.png" => "2f4e0764e8ba3946f52e2b727ce5986776a8a0de",
|
||||
"pending-48x48.png" => "4e3da1b2be713426b48ddddbd4ead386aadec461",
|
||||
"pending-75x75.png" => "6ab927863f95d37af1546d31e3bf8b096cc9ed4a",
|
||||
|
||||
"rendering-768x432.png" => "ed084f98f4b5855e9e0d95d6801753ba7357ca18",
|
||||
"rendering-100x100.png" => "b67cc4a3d126f29a0c11e7cba3843e6aceadb769",
|
||||
"rendering-110x110.png" => "d059575ffed532648d3dcf6b1429defcc98fc8b1",
|
||||
"rendering-250x250.png" => "9794c31aa3c4779f9cb2c541cedf2c25fa3397fe",
|
||||
"rendering-352x352.png" => "f523775cc3da917e15c3b15e4165fee2562c0ff1",
|
||||
"rendering-420x420.png" => "a9e786b5c339f29f9016d21858bf22c54146855c",
|
||||
"rendering-48x48.png" => "d7a9b5d7044636d3011541634aee43ca4a86ade6",
|
||||
"rendering-75x75.png" => "fa2ec2e53a4d50d9103a6e4370a3299ba5391544",
|
||||
|
||||
"unapproved-768x432.png" => "ebb176be0c34a9c0e18a3da139a81fa6cf2d8f10",
|
||||
"unapproved-100x100.png" => "d4b4b1f0518597bafcd9cf342b6466275db34bbc",
|
||||
"unapproved-110x110.png" => "7ad17e54cf834efd298d76c8799f58daf9c6829f",
|
||||
"unapproved-250x250.png" => "cddec9d17ee3afc5da51d2fbf8011e562362e39a",
|
||||
"unapproved-352x352.png" => "509b6c7bdb121e4185662987096860dd7f54ae11",
|
||||
"unapproved-420x420.png" => "f31bc4f3d5008732f91ac90608d4e77fcd8d8d2b",
|
||||
"unapproved-48x48.png" => "82da22ba47414d25ee544a253f6129d106cf17ef",
|
||||
"unapproved-75x75.png" => "13ad6ad9ab4f84f03c58165bc8468a181d07339c"
|
||||
];
|
||||
|
||||
static function GetCDNLocation($Location, $Extension = "png")
|
||||
{
|
||||
$ThumbnailHash = sha1_file($Location) . ".{$Extension}";
|
||||
$CDNLocation = $_SERVER["DOCUMENT_ROOT"]."/../polygoncdn/{$ThumbnailHash}";
|
||||
|
||||
if (!file_exists($CDNLocation)) self::UploadToCDN($Location);
|
||||
return self::$BaseURL.$ThumbnailHash;
|
||||
}
|
||||
|
||||
static function GetStatus($status, $x = 420, $y = 420)
|
||||
{
|
||||
$ImageName = "{$status}-{$x}x{$y}.png";
|
||||
if (!isset(self::$StatusThumbnails[$ImageName])) $ImageName = "{$status}-420x420.png";
|
||||
|
||||
return self::$BaseURL.self::$StatusThumbnails[$ImageName].".png";
|
||||
}
|
||||
|
||||
static function UploadToCDN($Location, $Extension = "png")
|
||||
{
|
||||
$Hash = sha1_file($Location);
|
||||
file_put_contents($_SERVER["DOCUMENT_ROOT"]."/../polygoncdn/{$Hash}.{$Extension}", file_get_contents($Location));
|
||||
}
|
||||
|
||||
static function DeleteFromCDN($Location)
|
||||
{
|
||||
$ThumbnailHash = sha1_file($Location);
|
||||
$CDNLocation = $_SERVER["DOCUMENT_ROOT"]."/../polygoncdn/{$ThumbnailHash}.png";
|
||||
|
||||
if (file_exists($CDNLocation)) unlink($CDNLocation);
|
||||
}
|
||||
|
||||
static function GetAsset($SQLResult, $x = 420, $y = 420, $Force = false)
|
||||
{
|
||||
// for this we need to pass in an sql pdo result
|
||||
// this is so we can check if the asset is under review or disapproved
|
||||
// passing in the sql result here saves us from having to do another query
|
||||
// if we implement hash caching then we'd also use this for that
|
||||
|
||||
$AssetID = $SQLResult->id;
|
||||
$Location = SITE_CONFIG['paths']['thumbs_assets']."{$AssetID}-{$x}x{$y}.png";
|
||||
|
||||
if ($Force) $SQLResult->approved = 1;
|
||||
|
||||
if ($SQLResult->approved == 0) return self::GetStatus("pending", $x, $y);
|
||||
if ($SQLResult->approved == 2) return self::GetStatus("unapproved", $x, $y);
|
||||
|
||||
if (!file_exists($Location)) return self::GetStatus("rendering", $x, $y);
|
||||
if ($SQLResult->approved == 1) return self::GetCDNLocation($Location);
|
||||
|
||||
return self::GetStatus("rendering", $x, $y);
|
||||
}
|
||||
|
||||
static function GetAssetFromID($AssetID, $x = 420, $y = 420, $force = false)
|
||||
{
|
||||
// primarily used for fetching group emblems
|
||||
// we dont need to block this as group emblems are fine to show publicly
|
||||
|
||||
$AssetInfo = db::run("SELECT * FROM assets WHERE id = :id", [":id" => $AssetID]);
|
||||
if(!$AssetInfo->rowCount()) return false;
|
||||
return self::GetAsset($AssetInfo->fetch(PDO::FETCH_OBJ), $x, $y, $force);
|
||||
}
|
||||
|
||||
static function GetAvatar($avatarID, $x = 420, $y = 420, $force = false)
|
||||
{
|
||||
if(!$force && !SESSION && GetUserAgent() != "Roblox/WinInet")
|
||||
return self::GetStatus("rendering", $x, $y);
|
||||
|
||||
$Location = SITE_CONFIG['paths']['thumbs_avatars']."{$avatarID}-{$x}x{$y}.png";
|
||||
|
||||
if(!file_exists($Location)) return self::GetStatus("rendering", $x, $y);
|
||||
return self::GetCDNLocation($Location);
|
||||
}
|
||||
|
||||
static function UploadAsset($handle, $assetID, $x, $y, $additionalOptions = [])
|
||||
{
|
||||
Polygon::ImportClass("Image");
|
||||
|
||||
$options = ["name" => "{$assetID}-{$x}x{$y}.png", "x" => $x, "y" => $y, "dir" => "thumbs/assets/"];
|
||||
$options = array_merge($options, $additionalOptions);
|
||||
|
||||
$Processed = Image::Process($handle, $options);
|
||||
if ($Processed !== true) throw new Exception($Processed);
|
||||
|
||||
self::UploadToCDN(SITE_CONFIG['paths']['thumbs_assets']."{$assetID}-{$x}x{$y}.png");
|
||||
}
|
||||
|
||||
static function DeleteAsset($AssetID)
|
||||
{
|
||||
$Thumbnails = glob(SITE_CONFIG["paths"]["thumbs_assets"]."{$AssetID}-*.png");
|
||||
|
||||
foreach ($Thumbnails as $Thumbnail)
|
||||
{
|
||||
self::DeleteFromCDN($Thumbnail);
|
||||
unlink($Thumbnail);
|
||||
}
|
||||
}
|
||||
|
||||
static function UploadAvatar($handle, $avatarID, $x, $y)
|
||||
{
|
||||
Polygon::ImportClass("Image");
|
||||
|
||||
Image::Process($handle, ["name" => "{$avatarID}-{$x}x{$y}.png", "x" => $x, "y" => $y, "dir" => "thumbs/avatars/"]);
|
||||
self::UploadToCDN(SITE_CONFIG['paths']['thumbs_avatars']."{$avatarID}-{$x}x{$y}.png");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
class TwoFactorAuth
|
||||
{
|
||||
static function Initialize()
|
||||
{
|
||||
require ROOT.'/api/private/vendors/2fa/FixedBitNotation.php';
|
||||
require ROOT.'/api/private/vendors/2fa/GoogleQrUrl.php';
|
||||
require ROOT.'/api/private/vendors/2fa/GoogleAuthenticatorInterface.php';
|
||||
require ROOT.'/api/private/vendors/2fa/GoogleAuthenticator.php';
|
||||
return new \Google\Authenticator\GoogleAuthenticator();
|
||||
}
|
||||
|
||||
static function Toggle()
|
||||
{
|
||||
if(!SESSION) return false;
|
||||
|
||||
db::run(
|
||||
"UPDATE users SET twofa = :2fa WHERE id = :uid",
|
||||
[":2fa" => (int)!SESSION["user"]["twofa"], ":uid" => SESSION["user"]["id"]]
|
||||
);
|
||||
}
|
||||
|
||||
static function GenerateRecoveryCodes()
|
||||
{
|
||||
if(!SESSION) return false;
|
||||
|
||||
$codes = str_split(bin2hex(random_bytes(60)), 12);
|
||||
db::run(
|
||||
"UPDATE users SET twofaRecoveryCodes = :json WHERE id = :uid",
|
||||
[":json" => json_encode(array_fill_keys($codes, true)), ":uid" => SESSION["user"]["id"]]
|
||||
);
|
||||
return $codes;
|
||||
}
|
||||
|
||||
static function GenerateNewSecret($GoogleAuthenticator)
|
||||
{
|
||||
if(!SESSION) return false;
|
||||
|
||||
$secret = $GoogleAuthenticator->generateSecret();
|
||||
db::run(
|
||||
"UPDATE users SET twofaSecret = :secret WHERE id = :uid",
|
||||
[":secret" => $secret, ":uid" => SESSION["user"]["id"]]
|
||||
);
|
||||
return $secret;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
try
|
||||
{
|
||||
$pdo = new PDO(
|
||||
'mysql:host='.SITE_CONFIG["database"]["host"].';
|
||||
dbname='.SITE_CONFIG["database"]["schema"].';
|
||||
charset=utf8mb4',
|
||||
SITE_CONFIG["database"]["username"],
|
||||
SITE_CONFIG["database"]["password"]
|
||||
);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
catch(PDOException $e)
|
||||
{
|
||||
echo "Project Polygon is currently undergoing maintenance. We will be back soon!";
|
||||
if(isset($_GET['showError'])) echo $e->getMessage();
|
||||
die();
|
||||
}
|
||||
|
||||
class db
|
||||
{
|
||||
static function run($sql, $params = false)
|
||||
{
|
||||
global $pdo;
|
||||
if(!$params) return $pdo->query($sql);
|
||||
|
||||
$query = $pdo->prepare($sql);
|
||||
|
||||
foreach ($params as $param => $value)
|
||||
{
|
||||
$query->bindValue($param, $value, is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR);
|
||||
}
|
||||
|
||||
$query->execute();
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
<?php
|
||||
|
||||
class PageBuilder
|
||||
{
|
||||
public static string $FooterAdditions = "";
|
||||
|
||||
public static array $Scripts =
|
||||
[
|
||||
"https://code.jquery.com/jquery-3.0.0.min.js",
|
||||
"/js/toastr.js"
|
||||
];
|
||||
|
||||
// this is separate from js dependencies as these MUST be loaded at the bottom
|
||||
public static array $PolygonScripts = [];
|
||||
|
||||
public static array $Stylesheets =
|
||||
[
|
||||
"/css/fontawesome-pro-v5.15.2/css/all.css",
|
||||
"/css/toastr.css"
|
||||
];
|
||||
|
||||
public static array $Config =
|
||||
[
|
||||
"title" => false,
|
||||
"Theme" => "light",
|
||||
"ShowNavbar" => true,
|
||||
"ShowFooter" => true,
|
||||
"AppAttributes" => ["class" => "app container py-4 nav-content"]
|
||||
];
|
||||
|
||||
public static array $MetaTags =
|
||||
[
|
||||
"viewport" => "width=device-width, initial-scale=1",
|
||||
"polygon-csrf" => SESSION ? SESSION["csrfToken"] : "false",
|
||||
"theme-color" => "#eb4034",
|
||||
"og:type" => "Website",
|
||||
"og:url" => "https://polygon.pizzaboxer.xyz",
|
||||
"og:site_name" => SITE_CONFIG["site"]["name"],
|
||||
"og:description" => "yeah its a website about shapes and squares and triangles and stuff and ummmmm",
|
||||
"og:image" => "https://polygon.pizzaboxer.xyz/img/PolygonChristmas.png"
|
||||
];
|
||||
|
||||
public static array $TemplateVariables =
|
||||
[
|
||||
"Announcements" => [],
|
||||
"Markdown" => false,
|
||||
"PendingAssets" => 0,
|
||||
"ErrorTitle" => "",
|
||||
"ErrorMessage" => ""
|
||||
];
|
||||
|
||||
static function ImportTemplate($Template)
|
||||
{
|
||||
if (!file_exists(ROOT . "/api/private/components/templates/{$Template}.php")) return false;
|
||||
|
||||
require ROOT . "/api/private/components/templates/{$Template}.php";
|
||||
}
|
||||
|
||||
static function AddResource(&$ResourceList, $Resource, $Cache = true, $PushToFirst = false)
|
||||
{
|
||||
if (substr($Resource, 0, 1) != "/") $Cache = false;
|
||||
|
||||
if ($Cache)
|
||||
{
|
||||
$Resource .= "?id=" . sha1_file(ROOT . $Resource);
|
||||
}
|
||||
|
||||
if ($PushToFirst)
|
||||
{
|
||||
array_unshift($ResourceList, $Resource);
|
||||
}
|
||||
else
|
||||
{
|
||||
$ResourceList[] = $Resource;
|
||||
}
|
||||
}
|
||||
|
||||
static function AddMetaTag($Property, $Content)
|
||||
{
|
||||
self::$MetaTags[$Property] = $Content;
|
||||
}
|
||||
|
||||
static function ShowStaticModal($options)
|
||||
{
|
||||
self::$FooterAdditions .= '<script type="text/javascript">$(function(){ polygon.buildModal('.json_encode($options).'); });</script>';
|
||||
}
|
||||
|
||||
static function BuildHeader()
|
||||
{
|
||||
self::$Config["Theme"] = "light";
|
||||
|
||||
if (!isset($_GET["DisableSnow"]) && $_SERVER["HTTP_HOST"] != "polygondev.pizzaboxer.xyz") self::$Scripts[] = "/js/snowstorm.js";
|
||||
|
||||
self::AddMetaTag("og:title", self::$Config["title"]);
|
||||
self::AddResource(PageBuilder::$PolygonScripts, "/js/polygon/core.js", true, true);
|
||||
self::AddResource(PageBuilder::$Stylesheets, "/css/polygon.css");
|
||||
|
||||
global $announcements, $markdown;
|
||||
|
||||
self::$TemplateVariables["Announcements"] = $announcements;
|
||||
self::$TemplateVariables["Markdown"] = $markdown;
|
||||
|
||||
if (SESSION)
|
||||
{
|
||||
if (SESSION["user"]["adminlevel"])
|
||||
{
|
||||
self::$TemplateVariables["PendingAssets"] = db::run("SELECT COUNT(*) FROM assets WHERE NOT approved AND type != 1")->fetchColumn();
|
||||
}
|
||||
|
||||
self::$Config["Theme"] = SESSION["user"]["theme"];
|
||||
}
|
||||
|
||||
self::AddResource(PageBuilder::$Stylesheets, "/css/polygon-" . self::$Config["Theme"] . ".css");
|
||||
|
||||
if (self::$Config["Theme"] == "2014")
|
||||
{
|
||||
self::AddResource(PageBuilder::$Scripts, "/js/polygon/Navigation2014.js");
|
||||
self::$Config["AppAttributes"]["ID"] = "navContent";
|
||||
|
||||
self::ImportTemplate("Head");
|
||||
self::ImportTemplate("Body2014");
|
||||
}
|
||||
else
|
||||
{
|
||||
self::ImportTemplate("Head");
|
||||
self::ImportTemplate("Body");
|
||||
}
|
||||
|
||||
ob_start();
|
||||
}
|
||||
|
||||
static function BuildFooter()
|
||||
{
|
||||
self::ImportTemplate("Footer");
|
||||
|
||||
ob_end_flush();
|
||||
}
|
||||
|
||||
static function errorCode($HTTPCode, $CustomMessage = false)
|
||||
{
|
||||
http_response_code($HTTPCode);
|
||||
|
||||
$Messages =
|
||||
[
|
||||
400 => ["title" => "Bad request", "text" => "There was a problem with your request"],
|
||||
404 => ["title" => "Requested page not found", "text" => "You may have clicked an expired link or mistyped the address"],
|
||||
420 => ["title" => "Website is currently under maintenance", "text" => "check back later"],
|
||||
500 => ["title" => "Unexpected error with your request", "text" => "Please try again after a few moments"]
|
||||
];
|
||||
|
||||
if (!isset($Messages[$HTTPCode])) $code = 500;
|
||||
if (is_array($CustomMessage) && count($CustomMessage)) $Messages[$HTTPCode] = $CustomMessage;
|
||||
|
||||
self::$TemplateVariables["ErrorTitle"] = $Messages[$HTTPCode]["title"];
|
||||
self::$TemplateVariables["ErrorMessage"] = $Messages[$HTTPCode]["text"];
|
||||
|
||||
self::BuildHeader();
|
||||
self::ImportTemplate("Error");
|
||||
self::BuildFooter();
|
||||
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
<body>
|
||||
<?php if (self::$Config["ShowNavbar"]) { ?>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark navbar-orange navbar-top py-0">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/"><?=SITE_CONFIG["site"]["name"]?></a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#primaryNavbar" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="primaryNavbar">
|
||||
<ul class="navbar-nav header-links mr-auto">
|
||||
<?php if (SESSION) { ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/games">Games</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/catalog">Catalog</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/develop">Develop</a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/forum">Forum</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/browse">People</a>
|
||||
</li>
|
||||
<?php if (SESSION) { ?>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="moreDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">More</a>
|
||||
<div class="dropdown-menu mt-0" aria-labelledby="moreDropdown">
|
||||
<!--a class="dropdown-item" href="/browse">People</a>
|
||||
<div class="dropdown-divider"></div-->
|
||||
<a class="dropdown-item" href="/discord">Discord</a>
|
||||
<!--a class="dropdown-item" href="https://twitter.com/boxerpizza">Twitter</a-->
|
||||
</div>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
<div class="navbar-nav">
|
||||
<?php if (SESSION) { ?>
|
||||
<a class="nav-link mr-2" href="/user?ID=<?=SESSION["user"]["id"]?>"><?=SESSION["user"]["username"]?></a>
|
||||
<?php if (Users::IsAdmin()) { ?>
|
||||
<div class="navbar-button-container">
|
||||
<a class="btn btn-sm btn-light py-0 px-1 unread-messages-indicator<?=!SESSION["unreadMessages"]?' d-none':''?>"><?=SESSION["unreadMessages"]?></a>
|
||||
<a class="btn btn-sm btn-outline-light my-1 mr-2" title="Messages" href="/my/messages" data-toggle="tooltip" data-html="true" data-placement="bottom">
|
||||
<i class="fas fa-envelope"></i>
|
||||
</a>
|
||||
</div>
|
||||
<?php } /* endif (Users::isAdmin()) */ ?>
|
||||
<div class="navbar-button-container">
|
||||
<a class="btn btn-sm btn-light py-0 px-1 friend-requests-indicator<?=!SESSION["friendRequests"]?' d-none':''?>"><?=SESSION["friendRequests"]?></a>
|
||||
<a class="btn btn-sm btn-outline-light my-1 mr-2" title="My Friends" href="/friends" data-toggle="tooltip" data-html="true" data-placement="bottom">
|
||||
<i class="fas fa-user-friends"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-button-container">
|
||||
<a class="btn btn-sm btn-outline-light my-1 mr-2" data-toggle="tooltip" data-html="true" data-placement="bottom" title="<?=SESSION["user"]["currency"]?> <?=SITE_CONFIG["site"]["currency"]?> <br> Next stipend in <?=GetReadableTime(SESSION["user"]["nextCurrencyStipend"], ["Full" => true, "Ending" => false, "Abbreviate" => true])?>" href="/my/money"><i class="fal fa-pizza-slice"></i> <?=SESSION["user"]["currency"]?></a>
|
||||
</div>
|
||||
<div class="navbar-button-container">
|
||||
<a class="btn btn-sm btn-light my-1 mr-2 px-3" href="/logout">Logout</a>
|
||||
</div>
|
||||
<?php } else { ?>
|
||||
<a class="nav-link" href="/">Sign Up</a>
|
||||
<span class="nav-link darken px-0">or</span>
|
||||
<a class="btn btn-sm btn-light my-1 mx-2 px-4" href="/login">Login</a>
|
||||
<?php } /* endif (SESSION) */ ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<?php if (SESSION) { ?>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark navbar-top bg-dark py-0">
|
||||
<div class="container">
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#secondaryNavbar" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="secondaryNavbar">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link py-1" href="/user">Profile</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link py-1" href="/my/character">Character</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link py-1" href="/friends">Friends</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link py-1" href="/groups">Groups</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link py-1" href="/my/stuff">Inventory</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link py-1" href="/my/money">Money</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link py-1" href="/my/account">Account</a>
|
||||
</li>
|
||||
<?php if (SESSION && SESSION["user"]["adminlevel"]) { ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link py-1" href="/admin">Admin <span class="btn btn-sm btn-outline-light py-0<?=self::$TemplateVariables["PendingAssets"] == 0 ? ' d-none' : ''?>" style="margin-top:-3px"><?=self::$TemplateVariables["PendingAssets"]?></span></a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<?php } /* endif (SESSION) */ ?>
|
||||
<noscript>
|
||||
<div class="alert py-2 mb-0 rounded-0 text-center text-light bg-danger" role="alert">
|
||||
disabling javascript breaks the ux in half so dont do it pls
|
||||
</div>
|
||||
</noscript>
|
||||
<?php foreach (self::$TemplateVariables["Announcements"] as $Announcement) { ?>
|
||||
<div class="alert py-2 mb-0 rounded-0 text-center text-<?=$Announcement["textcolor"]?>" role="alert" style="background-color: <?=$Announcement["bgcolor"]?>">
|
||||
<?=self::$TemplateVariables["Markdown"]->text($Announcement["text"])?>
|
||||
</div>
|
||||
<?php } /* foreach ($announcements as $announcement) */ ?>
|
||||
<?php } /* endif (self::$Config["ShowNavbar"]) */ ?>
|
||||
<div<?php foreach(self::$Config['AppAttributes'] as $Attribute => $Value) echo " {$Attribute}=\"{$Value}\""; ?>>
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
<body class="layout-2014 mt-5">
|
||||
<div class="nav-container no-gutter-ads">
|
||||
<div class="navigation" id="navigation" onselectstart="return false;">
|
||||
<div class="navigation-container">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="user">
|
||||
<div class="menu-item">
|
||||
<div class="username"><a href="/user?ID=<?=SESSION["user"]["id"]?>"><?=SESSION["user"]["username"]?></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav2014-my-roblox">
|
||||
<a class="menu-item" href="/home">
|
||||
<span class="icon"></span>Home
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav2014-profile">
|
||||
<a class="menu-item" href="/user">
|
||||
<span class="icon"></span>Profile
|
||||
</a>
|
||||
</li>
|
||||
<!--li class="nav2014-messages">
|
||||
<a class="menu-item" href="/user">
|
||||
<span class="icon"></span>Messages
|
||||
</a>
|
||||
</li-->
|
||||
<li class="nav2014-friends">
|
||||
<a class="menu-item" href="/friends">
|
||||
<span class="icon"></span>Friends
|
||||
<span class="notification-icon text-light friend-requests-indicator<?=!SESSION["friendRequests"]?' d-none':''?>"><?=SESSION["friendRequests"]?></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav2014-character">
|
||||
<a class="menu-item" href="/my/character">
|
||||
<span class="icon"></span>Character
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav2014-inventory">
|
||||
<a class="menu-item" href="/my/stuff">
|
||||
<span class="icon"></span>Inventory
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav2014-develop">
|
||||
<a class="menu-item" href="/develop">
|
||||
<span class="icon"></span>Develop
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav2014-groups">
|
||||
<a class="menu-item" href="/groups">
|
||||
<span class="icon"></span>Groups
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav2014-forum">
|
||||
<a class="menu-item" href="/forum">
|
||||
<span class="icon"></span>Forum
|
||||
</a>
|
||||
</li>
|
||||
<?php if (SESSION["user"]["adminlevel"]) { ?>
|
||||
<li class="nav2014-profile">
|
||||
<a class="menu-item" href="/admin">
|
||||
<span class="icon"></span>Admin
|
||||
<span class="notification-icon text-light <?=self::$TemplateVariables["PendingAssets"] == 0 ? ' d-none' : ''?>"><?=self::$TemplateVariables["PendingAssets"]?></span>
|
||||
</a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<!--li class="nav2014-blog">
|
||||
<a class="menu-item" href="https://web.archive.org/web/20140821165031/http://blog.roblox.com/">
|
||||
<span class="icon"></span>Blog
|
||||
</a>
|
||||
</li>
|
||||
<li class="upgrade-now">
|
||||
<a href="/web/20140821165031/http://www.roblox.com/Upgrades/BuildersClubMemberships.aspx" class="nav-button" id="builders-club-button">Upgrade Now</a>
|
||||
</li>
|
||||
<li class="nav2014-events">
|
||||
<span class="events-text">Events</span>
|
||||
</li>
|
||||
<li class="nav2014-sponsor">
|
||||
<a class="menu-item" href="/web/20140821165031/http://www.roblox.com/event/clanbattle" title="Clan Battle">
|
||||
<img src="https://web.archive.org/web/20140821165031im_/http://images.rbxcdn.com/6d4be1b1b1e67971c837da0dae72e82d">
|
||||
</a>
|
||||
</li-->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark navbar-orange header-2014 fixed-top py-0">
|
||||
<div class="nav-icon" onselectstart="return false;"></div>
|
||||
<a class="navbar-brand pt-0" href="/"><img src="/img/2013/roblox_logo.png"></a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#primaryNavbar" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="primaryNavbar">
|
||||
<ul class="navbar-nav header-links mr-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/games">Games</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/catalog">Catalog</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/develop">Develop</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/forum">Forum</a>
|
||||
</li>
|
||||
<div class="search">
|
||||
<div class="search-input-container">
|
||||
<input type="text" placeholder="Search">
|
||||
</div>
|
||||
<div class="search-icon"></div>
|
||||
<div class="universal-search-dropdown">
|
||||
<div class="universal-search-option" data-searchurl="/games?Keyword=">
|
||||
<div class="universal-search-text">Search <span class="universal-search-string"></span> in Games</div>
|
||||
</div>
|
||||
<div class="universal-search-option" data-searchurl="/browse?Category=People&SearchTextBox=">
|
||||
<div class="universal-search-text">Search <span class="universal-search-string"></span> in People</div>
|
||||
</div>
|
||||
<div class="universal-search-option selected" data-searchurl="/browse?Category=Groups&SearchTextBox=">
|
||||
<div class="universal-search-text">Search <span class="universal-search-string"></span> in Groups</div>
|
||||
</div>
|
||||
<div class="universal-search-option" data-searchurl="/catalog?Keyword=">
|
||||
<div class="universal-search-text">Search <span class="universal-search-string"></span> in Catalog</div>
|
||||
</div>
|
||||
<div class="universal-search-option" data-searchurl="/catalog?CurrencyType=0&SortType=1&Category=6&Keyword=">
|
||||
<div class="universal-search-text">Search <span class="universal-search-string"></span> in Library</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
<div class="navbar-nav">
|
||||
<div class="navbar-button-container">
|
||||
<a class="btn btn-sm mr-4" data-toggle="tooltip" data-html="true" data-placement="bottom" title="<?=SESSION["user"]["currency"]?> <?=SITE_CONFIG["site"]["currency"]?> <br> Next stipend in <?=GetReadableTime(SESSION["user"]["nextCurrencyStipend"], ["Full" => true, "Ending" => false, "Abbreviate" => true])?>" href="/my/money"><i class="fas fa-pizza-slice mr-1"></i> <?=SESSION["user"]["currency"]?></a>
|
||||
</div>
|
||||
<div class="navbar-button-container dropdown-hover">
|
||||
<a class="btn btn-sm" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fas fa-cog"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right mx-2" aria-labelledby="settings-dropdown">
|
||||
<a class="dropdown-item" href="/my/account">Settings</a>
|
||||
<a class="dropdown-item" href="/logout">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
<noscript>
|
||||
<div id="ctl00_Announcement">
|
||||
<div id="ctl00_SystemAlertDiv" class="SystemAlert" style="background-color:red">
|
||||
<div id="ctl00_SystemAlertTextColor" class="SystemAlertText">
|
||||
<div id="ctl00_LabelAnnouncement">Please enable Javascript to use all the features on this site.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
<?php foreach (self::$TemplateVariables["Announcements"] as $Announcement) { ?>
|
||||
<div id="ctl00_Announcement">
|
||||
<div id="ctl00_SystemAlertDiv" class="SystemAlert" style="background-color:<?=$Announcement["bgcolor"]?>">
|
||||
<div id="ctl00_SystemAlertTextColor" class="SystemAlertText">
|
||||
<div id="ctl00_LabelAnnouncement"><?=self::$TemplateVariables["Markdown"]->line($Announcement["text"])?></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } /* foreach($announcements as $announcement) */ ?>
|
||||
<div<?php foreach(self::$Config['AppAttributes'] as $Attribute => $Value) echo " {$Attribute}=\"{$Value}\""; ?>>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<div class="card mx-auto" style="max-width:640px;">
|
||||
<div class="card-body text-center">
|
||||
<img src="/img/error.png">
|
||||
<h2 class="font-weight-normal"><?=self::$TemplateVariables["ErrorTitle"]?></h2>
|
||||
<?=self::$TemplateVariables["ErrorMessage"]?>
|
||||
<hr>
|
||||
<a class="btn btn-outline-primary mx-1 mt-1 py-1" onclick="window.history.back()">Go to Previous Page</a>
|
||||
<a class="btn btn-outline-primary mx-1 mt-1 py-1" href="/">Return Home</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
</div>
|
||||
<?php if (self::$Config["ShowFooter"]) { ?>
|
||||
<nav class="footer navbar navbar-light navbar-orange">
|
||||
<div class="container py-2 text-light text-center">
|
||||
<div class="mx-auto">
|
||||
<p class="mt-1 mb-3"><a href="/info/terms-of-service" class="text-light px-2">Terms of Service</a> | <a href="/info/privacy" class="text-light px-2">Privacy Policy</a> | <a href="mailto:support@polygon.pizzaboxer.xyz" class="text-light px-2">Contact Us</a></p>
|
||||
<div class="divider-bottom"></div>
|
||||
<p class="mt-2 mb-0"><small class="px-2">© Project Polygon 2021. We are in no way associated with Roblox Corporation.</small></p>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<?php } ?>
|
||||
<div class="global modal fade" tabindex="-1" role="dialog" aria-labelledby="primaryModalCenter" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header card-header bg-cardpanel py-2">
|
||||
<h3 class="col-12 modal-title text-center font-weight-normal"></h3>
|
||||
</div>
|
||||
<div class="modal-body text-center text-break">
|
||||
your smell
|
||||
</div>
|
||||
<div class="modal-footer text-center">
|
||||
<div class="mx-auto">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php if (SESSION) { ?>
|
||||
<div class="placelauncher modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content"></div>
|
||||
</div>
|
||||
<div class="launch template d-none">
|
||||
<div class="modal-body text-center">
|
||||
<span class="jumbo spinner-border text-danger mb-3" role="status"></span>
|
||||
<h5 class="font-weight-normal mb-3">Starting <?=SITE_CONFIG["site"]["name"]?>...</h5>
|
||||
<a class="btn btn-sm btn-outline-danger btn-block px-4 cancel-join" data-dismiss="modal">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="install template d-none">
|
||||
<div class="modal-body text-center pb-0">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<img src="/img/PolygonChristmas.png" class="img-fluid pl-3 py-3 pr-1" style="max-width: 150px">
|
||||
<h2 class="font-weight-normal">Welcome to <?=SITE_CONFIG["site"]["name"]?>!</h2>
|
||||
<h5 class="font-weight-normal">Seems like you don't have <span class="year">2010</span> installed</h5>
|
||||
<a class="btn btn-success btn-block mx-auto mt-3 install" style="max-width:18rem">Download</a>
|
||||
</div>
|
||||
<div class="modal-footer text-center py-2">
|
||||
<small class="mx-auto">If you do have the client installed, just ignore this</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } // endif (SESSION) ?>
|
||||
<script src="/js/bootstrap.bundle.min.js"></script>
|
||||
<?php if (SESSION && SESSION["user"]["adminlevel"]){ ?>
|
||||
<script>
|
||||
//admin.js
|
||||
if (polygon.admin == undefined) polygon.admin = {};
|
||||
|
||||
polygon.admin.forum =
|
||||
{
|
||||
moderate_post_prompt: function(type, id)
|
||||
{
|
||||
polygon.buildModal({
|
||||
header: "Delete Post",
|
||||
body: 'Are you sure you want to delete this post?',
|
||||
buttons: [{class:'btn btn-danger px-4 post-delete-confirm', attributes:{'data-type':type, 'data-id':id}, dismiss:true, text:'Yes'}, {class:'btn btn-secondary px-4', dismiss:true, text:'No'}]
|
||||
});
|
||||
},
|
||||
|
||||
moderate_post: function(type, id)
|
||||
{
|
||||
$.post('/api/admin/delete-post', {"postType": type, "postId": id}, function(data)
|
||||
{
|
||||
if(data.success)
|
||||
{
|
||||
toastr["success"]("Post has been deleted");
|
||||
setTimeout(function(){ window.location.reload(); }, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
toastr["error"](data.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
polygon.admin.gitpull = function()
|
||||
{
|
||||
polygon.buildModal({
|
||||
header: "<i class=\"fab fa-git-alt text-danger\"></i> Git Pull",
|
||||
body: "<span class=\"spinner-border spinner-border-sm text-danger\" role=\"status\" aria-hidden=\"true\"></span> Executing Git Pull...",
|
||||
buttons:
|
||||
[
|
||||
{class: 'btn btn-outline-primary', dismiss: true, text: 'Close'},
|
||||
{class: 'btn btn-outline-danger disabled', attributes: {"disabled":"disabled"}, text: 'Run Again'}
|
||||
]
|
||||
});
|
||||
|
||||
$.get("/api/admin/git-pull", function(data)
|
||||
{
|
||||
polygon.buildModal({
|
||||
header: "<i class=\"fab fa-git-alt text-danger\"></i> Git Pull",
|
||||
body: "<pre class=\"mb-0\">"+data+"</pre>",
|
||||
buttons:
|
||||
[
|
||||
{class: 'btn btn-outline-primary', dismiss: true, text: 'Close'},
|
||||
{class: 'btn btn-outline-success gitpull', text: 'Run Again'}
|
||||
]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$("body").on("click", ".gitpull", polygon.admin.gitpull);
|
||||
|
||||
$("body").keydown(function(event)
|
||||
{
|
||||
if (event.originalEvent.ctrlKey && event.originalEvent.key == "/") polygon.admin.gitpull();
|
||||
});
|
||||
|
||||
polygon.admin.request_render = function(type, id)
|
||||
{
|
||||
$.post('/api/admin/request-render', {"renderType": type, "assetID": id}, function(data)
|
||||
{
|
||||
if(data.success) toastr["success"](data.message);
|
||||
else toastr["error"](data.message);
|
||||
});
|
||||
}
|
||||
|
||||
$("body").on("click", ".post-delete", function(){ polygon.admin.forum.moderate_post_prompt($(this).attr("data-type"), $(this).attr("data-id")); });
|
||||
$("body").on("click", ".post-delete-confirm", function(){ polygon.admin.forum.moderate_post($(this).attr("data-type"), $(this).attr("data-id")); });
|
||||
$("body").on("click", ".request-render", function(){ polygon.admin.request_render($(this).attr("data-type"), $(this).attr("data-id")); });
|
||||
</script>
|
||||
<?php } /* endif (SESSION && SESSION["user"]["adminlevel"]) */ ?>
|
||||
<?php foreach(self::$PolygonScripts as $url){ ?>
|
||||
<script type="text/javascript" src="<?=$url?>"></script>
|
||||
<?php } ?>
|
||||
<?=self::$FooterAdditions?>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title><?=(self::$Config["title"] ? self::$Config["title"] . " - " : "") . SITE_CONFIG["site"]["name"]?></title>
|
||||
<link rel='shortcut icon' type='image/x-icon' href='/img/ProjectPolygon.ico' />
|
||||
<meta charset="utf-8">
|
||||
<?php foreach (self::$MetaTags as $Property => $Content) { ?>
|
||||
<meta <?=substr($Property, 0, 2) == "og" ? "property" : "name"?>="<?=$Property?>" content="<?=$Content?>">
|
||||
<?php } ?>
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
|
||||
<?php foreach (self::$Stylesheets as $url) { ?>
|
||||
<link rel="stylesheet" href="<?=$url?>">
|
||||
<?php } foreach (self::$Scripts as $url) { ?>
|
||||
<script type="text/javascript" src="<?=$url?>"></script>
|
||||
<?php } ?>
|
||||
<script>
|
||||
var polygon = {};
|
||||
|
||||
polygon.user =
|
||||
{
|
||||
<?php if (SESSION) { ?>
|
||||
logged_in: true,
|
||||
name: "<?=SESSION["user"]["username"]?>",
|
||||
id: <?=SESSION["user"]["id"]?>,
|
||||
money: <?=SESSION["user"]["currency"]?>,
|
||||
<?php } else { ?>
|
||||
logged_in: false,
|
||||
<?php } ?>
|
||||
theme: "<?=self::$Config["Theme"]?>"
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,77 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<soap:Body>
|
||||
<OpenJobEx xmlns="http://roblox.com/">
|
||||
<job>
|
||||
<id>{JobID}</id>
|
||||
<expirationInSeconds>30</expirationInSeconds>
|
||||
<category>1</category>
|
||||
<cores>1</cores>
|
||||
</job>
|
||||
<script>
|
||||
<name>RenderScript</name>
|
||||
<script>
|
||||
-- Avatar v1.0.2
|
||||
-- This is the thumbnail script for R6 avatars. Straight up and down, with the right arm out if they have a gear.
|
||||
|
||||
local baseUrl, thumbnailKey, renderType, assetId, synchronous = ...
|
||||
|
||||
local ThumbnailGenerator = game:GetService("ThumbnailGenerator")
|
||||
|
||||
pcall(function()
|
||||
game:GetService("ContentProvider"):SetBaseUrl(baseUrl)
|
||||
game:GetService("InsertService"):SetAssetUrl(baseUrl .. "/Asset/?id=%d")
|
||||
game:GetService("InsertService"):SetAssetVersionUrl(baseUrl .. "/Asset/?assetversionid=%d")
|
||||
end)
|
||||
|
||||
game:GetService("HttpService").HttpEnabled = true
|
||||
game:GetService("ScriptContext").ScriptsDisabled = true
|
||||
|
||||
print("[" .. game.JobId .. "] Starting new render for " .. renderType .. " ID " .. assetId)
|
||||
game:HttpPost(baseUrl .. "/api/render/update?ApiKey=" .. thumbnailKey .. "&RenderJobID=" .. game.JobId, '{"Status": 1}', synchronous, "text/plain", true)
|
||||
|
||||
local player = game:GetService("Players"):CreateLocalPlayer(0)
|
||||
player.CharacterAppearance = baseUrl .. "/Asset/CharacterFetch.ashx?userId=" .. assetId .. "&serverId=-1&t=" .. tick()
|
||||
player:LoadCharacter(false)
|
||||
|
||||
-- Raise up the character's arm if they have gear.
|
||||
local gear = player.Backpack:GetChildren()[1]
|
||||
if gear then
|
||||
gear.Parent = player.Character
|
||||
player.Character.Torso['Right Shoulder'].CurrentAngle = math.rad(90)
|
||||
end
|
||||
|
||||
local click = ThumbnailGenerator:Click("PNG", 1024, 1024, true)
|
||||
local clickObject = ThumbnailGenerator:Click("OBJ", 420, 420, true)
|
||||
|
||||
result = '{"Status": 2, "Click": "' .. tostring(click) .. '", "ClickObject": [' .. tostring(clickObject) .. ']}'
|
||||
print("[" .. game.JobId .. "] Successfully rendered, moving on...")
|
||||
|
||||
game:HttpPost(baseUrl .. "/api/render/update?ApiKey=" .. thumbnailKey .. "&RenderJobID=" .. game.JobId, result, synchronous, "text/plain", true)
|
||||
</script>
|
||||
<arguments>
|
||||
<LuaValue>
|
||||
<type>LUA_TSTRING</type>
|
||||
<value>{BaseURL}</value>
|
||||
</LuaValue>
|
||||
<LuaValue>
|
||||
<type>LUA_TSTRING</type>
|
||||
<value>{ThumbnailKey}</value>
|
||||
</LuaValue>
|
||||
<LuaValue>
|
||||
<type>LUA_TSTRING</type>
|
||||
<value>{RenderType}</value>
|
||||
</LuaValue>
|
||||
<LuaValue>
|
||||
<type>LUA_TNUMBER</type>
|
||||
<value>{AssetID}</value>
|
||||
</LuaValue>
|
||||
<LuaValue>
|
||||
<type>LUA_TBOOLEAN</type>
|
||||
<value>{Synchronous}</value>
|
||||
</LuaValue>
|
||||
</arguments>
|
||||
</script>
|
||||
</OpenJobEx>
|
||||
</soap:Body>
|
||||
</soap:Envelope>
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<soap:Body>
|
||||
<OpenJobEx xmlns="http://roblox.com/">
|
||||
<job>
|
||||
<id>{JobID}</id>
|
||||
<expirationInSeconds>30</expirationInSeconds>
|
||||
<category>1</category>
|
||||
<cores>1</cores>
|
||||
</job>
|
||||
<script>
|
||||
<name>RenderScript</name>
|
||||
<script>
|
||||
-- Avatar v1.0.2
|
||||
-- This is the thumbnail script for R6 avatars. Straight up and down, with the right arm out if they have a gear.
|
||||
|
||||
local baseUrl, thumbnailKey, renderType, assetId, synchronous = ...
|
||||
|
||||
local ThumbnailGenerator = game:GetService("ThumbnailGenerator")
|
||||
|
||||
pcall(function()
|
||||
game:GetService("ContentProvider"):SetBaseUrl(baseUrl)
|
||||
game:GetService("InsertService"):SetAssetUrl(baseUrl .. "/Asset/?id=%d")
|
||||
game:GetService("InsertService"):SetAssetVersionUrl(baseUrl .. "/Asset/?assetversionid=%d")
|
||||
end)
|
||||
|
||||
game:GetService("HttpService").HttpEnabled = true
|
||||
game:GetService("ScriptContext").ScriptsDisabled = true
|
||||
|
||||
print("[" .. game.JobId .. "] Starting new render for " .. renderType .. " ID " .. assetId)
|
||||
game:HttpPost(baseUrl .. "/api/render/update?ApiKey=" .. thumbnailKey .. "&RenderJobID=" .. game.JobId, '{"Status": 1}', synchronous, "text/plain", true)
|
||||
|
||||
local player = game:GetService("Players"):CreateLocalPlayer(0)
|
||||
player.CharacterAppearance = baseUrl .. "/api/render/characterasset?id=" .. assetId
|
||||
player:LoadCharacter(false)
|
||||
|
||||
-- Raise up the character's arm if they have gear.
|
||||
local gear = player.Backpack:GetChildren()[1]
|
||||
if gear then
|
||||
gear.Parent = player.Character
|
||||
player.Character.Torso['Right Shoulder'].CurrentAngle = math.rad(90)
|
||||
end
|
||||
|
||||
local click = ThumbnailGenerator:Click("PNG", 1024, 1024, true)
|
||||
local clickObject = ThumbnailGenerator:Click("OBJ", 420, 420, true)
|
||||
|
||||
result = '{"Status": 2, "Click": "' .. tostring(click) .. '", "ClickObject": [' .. tostring(clickObject) .. ']}'
|
||||
print("[" .. game.JobId .. "] Successfully rendered, moving on...")
|
||||
|
||||
game:HttpPost(baseUrl .. "/api/render/update?ApiKey=" .. thumbnailKey .. "&RenderJobID=" .. game.JobId, result, synchronous, "text/plain", true)
|
||||
</script>
|
||||
<arguments>
|
||||
<LuaValue>
|
||||
<type>LUA_TSTRING</type>
|
||||
<value>{BaseURL}</value>
|
||||
</LuaValue>
|
||||
<LuaValue>
|
||||
<type>LUA_TSTRING</type>
|
||||
<value>{ThumbnailKey}</value>
|
||||
</LuaValue>
|
||||
<LuaValue>
|
||||
<type>LUA_TSTRING</type>
|
||||
<value>{RenderType}</value>
|
||||
</LuaValue>
|
||||
<LuaValue>
|
||||
<type>LUA_TNUMBER</type>
|
||||
<value>{AssetID}</value>
|
||||
</LuaValue>
|
||||
<LuaValue>
|
||||
<type>LUA_TBOOLEAN</type>
|
||||
<value>{Synchronous}</value>
|
||||
</LuaValue>
|
||||
</arguments>
|
||||
</script>
|
||||
</OpenJobEx>
|
||||
</soap:Body>
|
||||
</soap:Envelope>
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<soap:Body>
|
||||
<OpenJobEx xmlns="http://roblox.com/">
|
||||
<job>
|
||||
<id>{JobID}</id>
|
||||
<expirationInSeconds>30</expirationInSeconds>
|
||||
<category>1</category>
|
||||
<cores>1</cores>
|
||||
</job>
|
||||
<script>
|
||||
<name>RenderScript</name>
|
||||
<script>
|
||||
-- Head v1.0.2
|
||||
|
||||
local baseUrl, thumbnailKey, renderType, assetId, synchronous = ...
|
||||
|
||||
local ThumbnailGenerator = game:GetService("ThumbnailGenerator")
|
||||
|
||||
pcall(function()
|
||||
game:GetService("ContentProvider"):SetBaseUrl(baseUrl)
|
||||
game:GetService("InsertService"):SetAssetUrl(baseUrl .. "/Asset/?id=%d")
|
||||
game:GetService("InsertService"):SetAssetVersionUrl(baseUrl .. "/Asset/?assetversionid=%d")
|
||||
end)
|
||||
|
||||
game:GetService("HttpService").HttpEnabled = true
|
||||
game:GetService("ScriptContext").ScriptsDisabled = true
|
||||
|
||||
print("[" .. game.JobId .. "] Starting new render for " .. renderType .. " ID " .. assetId)
|
||||
game:HttpPost(baseUrl .. "/api/render/update?ApiKey=" .. thumbnailKey .. "&RenderJobID=" .. game.JobId, '{"Status": 1}', synchronous, "text/plain", true)
|
||||
|
||||
local player = game:GetService("Players"):CreateLocalPlayer(0)
|
||||
player.CharacterAppearance = baseUrl .. "/api/render/characterasset?id=" .. assetId
|
||||
player:LoadCharacter(false)
|
||||
|
||||
for _, bodyPart in ipairs(player.Character:GetChildren()) do
|
||||
if bodyPart.className == "Part" and bodyPart.Name ~= "Head" then
|
||||
bodyPart:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
local click = ThumbnailGenerator:Click("PNG", 1024, 1024, true)
|
||||
local clickObject = ThumbnailGenerator:Click("OBJ", 420, 420, true)
|
||||
|
||||
result = '{"Status": 2, "Click": "' .. tostring(click) .. '", "ClickObject": [' .. tostring(clickObject) .. ']}'
|
||||
print("[" .. game.JobId .. "] Successfully rendered, moving on...")
|
||||
|
||||
game:HttpPost(baseUrl .. "/api/render/update?ApiKey=" .. thumbnailKey .. "&RenderJobID=" .. game.JobId, result, synchronous, "text/plain", true)
|
||||
</script>
|
||||
<arguments>
|
||||
<LuaValue>
|
||||
<type>LUA_TSTRING</type>
|
||||
<value>{BaseURL}</value>
|
||||
</LuaValue>
|
||||
<LuaValue>
|
||||
<type>LUA_TSTRING</type>
|
||||
<value>{ThumbnailKey}</value>
|
||||
</LuaValue>
|
||||
<LuaValue>
|
||||
<type>LUA_TSTRING</type>
|
||||
<value>{RenderType}</value>
|
||||
</LuaValue>
|
||||
<LuaValue>
|
||||
<type>LUA_TNUMBER</type>
|
||||
<value>{AssetID}</value>
|
||||
</LuaValue>
|
||||
<LuaValue>
|
||||
<type>LUA_TBOOLEAN</type>
|
||||
<value>{Synchronous}</value>
|
||||
</LuaValue>
|
||||
</arguments>
|
||||
</script>
|
||||
</OpenJobEx>
|
||||
</soap:Body>
|
||||
</soap:Envelope>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue