A few rendering updates for templating scripts and some admin page stuff.

This commit is contained in:
Graphictoria 2022-08-19 19:48:11 -04:00
parent 6c939f4c2d
commit 57fcc25822
31 changed files with 1560 additions and 109 deletions

View File

@ -10,6 +10,9 @@ namespace App\Grid;
use Illuminate\Support\Facades\Storage;
use SoapClient;
use App\Models\ArbiterSoapFault;
use App\Helpers\GridHelper;
class SoapService
{
/**
@ -27,11 +30,12 @@ class SoapService
* @param string $arbiterAddr
* @return null
*/
public function __construct($arbiterAddr) {
public function __construct($arbiterType) {
$arbiter = GridHelper::{strtolower($arbiterType) . 'Arbiter'}();
$this->Client = new SoapClient(
Storage::path('grid/RCCService.wsdl'),
Storage::path('grid/RCCservice.wsdl'), // Arbiter WCF service WSDL should not be used for RCCService calls.
[
'location' => $arbiterAddr,
'location' => $arbiter,
'uri' => 'http://roblox.com/',
'exceptions' => false
]
@ -49,72 +53,17 @@ class SoapService
$soapResult = $this->Client->{$name}($args);
if(is_soap_fault($soapResult)) {
// TODO: XlXi: log faults
ArbiterSoapFault::create([
'function' => $name,
'code' => $soapResult->getCode(),
'message' => $soapResult->getMessage(),
'job_arguments' => json_encode($args)
]);
}
return $soapResult;
}
/* Job constructors */
public static function LuaValue($value)
{
switch ($value) {
case is_bool(json_encode($value)) || $value == 1:
return json_encode($value);
default:
return $value;
}
}
public static function CastType($value)
{
$luaTypeConversions = [
'NULL' => 'LUA_TNIL',
'boolean' => 'LUA_TBOOLEAN',
'integer' => 'LUA_TNUMBER',
'double' => 'LUA_TNUMBER',
'string' => 'LUA_TSTRING',
'array' => 'LUA_TTABLE',
'object' => 'LUA_TNIL'
];
return $luaTypeConversions[gettype($value)];
}
public static function ToLuaArguments($luaArguments = [])
{
$luaValue = ['LuaValue' => []];
foreach ($luaArguments as $argument) {
array_push(
$luaValue['LuaValue'],
[
'type' => SoapService::CastType($argument),
'value' => SoapService::LuaValue($argument)
]
);
}
return $luaValue;
}
public static function MakeJobJSON($jobID, $expiration, $category, $cores, $scriptName, $script, $scriptArgs = [])
{
return [
'job' => [
'id' => $jobID,
'expirationInSeconds' => $expiration,
'category' => $category,
'cores' => $cores
],
'script' => [
'name' => $scriptName,
'script' => $script,
'arguments' => SoapService::ToLuaArguments($scriptArgs)
]
];
}
/* Service functions */
public function HelloWorld()

View File

@ -7,6 +7,7 @@
namespace App\Helpers;
use Illuminate\Support\Facades\Storage;
use Illuminate\Http\Request;
use App\Models\DynamicWebConfiguration;
@ -31,13 +32,113 @@ class GridHelper
public static function hasAllAccess()
{
if(app()->runningInConsole()) return true;
if(GridHelper::isIpWhitelisted() && GridHelper::isAccessKeyValid()) return true;
if(self::isIpWhitelisted() && self::isAccessKeyValid()) return true;
return false;
}
public static function createScript($scripts = [], $arguments = [])
public static function LuaValue($value)
{
// TODO: XlXi: this when we get the grid working with the site
switch ($value) {
case is_bool(json_encode($value)) || $value == 1:
return json_encode($value);
default:
return $value;
}
}
public static function CastType($value)
{
$luaTypeConversions = [
'NULL' => 'LUA_TNIL',
'boolean' => 'LUA_TBOOLEAN',
'integer' => 'LUA_TNUMBER',
'double' => 'LUA_TNUMBER',
'string' => 'LUA_TSTRING',
'array' => 'LUA_TTABLE',
'object' => 'LUA_TNIL'
];
return $luaTypeConversions[gettype($value)];
}
public static function ToLuaArguments($luaArguments = [])
{
$luaValue = ['LuaValue' => []];
foreach ($luaArguments as $argument) {
array_push(
$luaValue['LuaValue'],
[
'type' => self::CastType($argument),
'value' => self::LuaValue($argument)
]
);
}
return $luaValue;
}
public static function Job($jobID, $expiration, $category, $cores, $scriptName, $script, $scriptArgs = [])
{
return [
'job' => [
'id' => $jobID,
'expirationInSeconds' => $expiration,
'category' => $category,
'cores' => $cores
],
'script' => [
'name' => $scriptName,
'script' => $script,
'arguments' => self::ToLuaArguments($scriptArgs)
]
];
}
public static function JobTemplate($jobID, $expiration, $category, $cores, $scriptName, $templateName, $scriptArgs = [])
{
$disk = Storage::build([
'driver' => 'local',
'root' => storage_path('app/grid/scripts'),
]);
$fileName = sprintf('%s.lua', $templateName);
if(!$disk->exists($fileName))
throw new Exception('Unable to locate template file.');
$job = self::Job($jobID, $expiration, $category, $cores, $scriptName, '', $scriptArgs);
$job['script']['script'] = $disk->get($fileName);
return $job;
}
public static function getArbiter($name)
{
$query = DynamicWebConfiguration::where('name', sprintf('%sArbiterIP', $name))->first();
if(!$query)
throw new Exception('Unknown arbiter.');
return $query->value;
}
public static function gameArbiter()
{
return sprintf('http://%s:64989', self::getArbiter('Game'));
}
public static function thumbnailArbiter()
{
return sprintf('http://%s:64989', self::getArbiter('Thumbnail'));
}
public static function gameArbiterMonitor()
{
return sprintf('http://%s:64990', self::getArbiter('Game'));
}
public static function thumbnailArbiterMonitor()
{
return sprintf('http://%s:64990', self::getArbiter('Thumbnail'));
}
}

View File

@ -49,7 +49,7 @@ class ThumbnailController extends Controller
$tracker->target = $valid['id'];
$tracker->save();
ArbiterRender::dispatch($tracker, $valid['type'] == '3d');
ArbiterRender::dispatch($tracker, $valid['type'] == '3d', $asset->typeString(), $asset->id);
}
return response(['status' => 'loading']);

View File

@ -0,0 +1,27 @@
<?php
namespace App\Http\Controllers\Web;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class AdminController extends Controller
{
// Moderator+
function dashboard()
{
//
}
// Admin+
function arbiterDiag(Request $request, string $arbiterType = null)
{
if($arbiterType == null)
return view('web.admin.diag.picker');
return view('web.admin.diag')->with([
'title' => sprintf('%s Arbiter Diag', $arbiterType),
'arbiter' => $arbiterType
]);
}
}

View File

@ -5,6 +5,7 @@ namespace App\Http\Controllers\Web;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use App\Helpers\GridHelper;
use App\Http\Controllers\Controller;
use App\Grid\SoapService;
@ -15,20 +16,10 @@ class TestController extends Controller
*/
public function generateThumbnail()
{
$testScript = <<<TestScript
settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4;
game:GetService("ContentProvider"):SetThreadPool(16)
game:GetService("Stats"):SetReportUrl("http://api.gtoria.net/teststat")
// mrgrey = https://www.roblox.com/asset/?id=1785197
local p = game:GetService("Players"):CreateLocalPlayer(0)
p.CharacterAppearance = "http://api.gtoria.net/user/getCharacter.php?key=D869593BF742A42F79915993EF1DB&mode=ch&sid=1&uid=1"
p:LoadCharacter(false)
return game:GetService("ThumbnailGenerator"):Click("PNG", 420, 420, true, false)
TestScript;
$test = new SoapService('http://192.168.0.3:64989');
$result = $test->OpenJob(SoapService::MakeJobJSON('test', 10, 0, 0, 'test render', $testScript));
$test = new SoapService('Thumbnail');
$result = $test->OpenJob(GridHelper::JobTemplate('test', 10, 0, 0, 'test render', 'place', ['http://www.roblox.com/asset/?id=444204653', 'PNG', 1920, 1080, 'https://www.gtoria.local/', 169618721]));
return response(base64_decode($result->OpenJobExResult->LuaValue[0]->value))
->header('Content-Type', 'image/png');

View File

@ -69,6 +69,7 @@ class Kernel extends HttpKernel
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'roleset' => \App\Http\Middleware\Roleset::class,
'banned' => \App\Http\Middleware\CheckBan::class,
];
}

View File

@ -4,8 +4,9 @@ namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class Administrator
class Roleset
{
/**
* Handle an incoming request.
@ -14,11 +15,11 @@ class Administrator
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
public function handle(Request $request, Closure $next, $role)
{
if (Auth::user()->isAdmin())
if (Auth::check() && Auth::user()->hasRoleset($role))
return $next($request);
return abort(403);
return abort(404);
}
}

View File

@ -12,6 +12,7 @@ use Illuminate\Support\Str;
use App\Grid\SoapService;
use App\Helpers\CdnHelper;
use App\Helpers\GridHelper;
use App\Models\RenderTracker;
class ArbiterRender implements ShouldQueue
@ -45,16 +46,32 @@ class ArbiterRender implements ShouldQueue
* @var bool
*/
public $is3D;
/**
* Asset type string based on asset's type id
*
* @var string
*/
public $type;
/**
* Asset ID to render
*
* @var int
*/
public $assetId;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(RenderTracker $tracker, bool $is3D)
public function __construct(RenderTracker $tracker, bool $is3D, string $type, int $assetId)
{
$this->tracker = $tracker;
$this->is3D = $is3D;
$this->type = $type;
$this->assetId = $assetId;
}
/**
@ -64,28 +81,41 @@ class ArbiterRender implements ShouldQueue
*/
public function handle()
{
$testScript = <<<TestScript
settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4;
game:GetService("ContentProvider"):SetThreadPool(16)
game:GetService("Stats"):SetReportUrl("http://api.gtoria.net/reportstat?cock=1")
local Lighting = game:GetService("Lighting")
Lighting.ClockTime = 13
Lighting.GeographicLatitude = -5
$arguments = [
url(sprintf('/asset?id=%d', $this->assetId)), // TODO: XlXi: Move url() to route once the route actually exists.
($this->is3D ? 'OBJ' : 'PNG'),
840, // Width
840, // Height
url('/')
];
switch($this->type) {
case 'Head':
case 'Shirt':
case 'Pants':
case 'BodyPart':
// TODO: XlXi: Move this to config, as it could be different from prod in a testing environment. Also move this to it's own asset (not loading from roblox).
array_push($arguments, 'https://www.roblox.com/asset/?id=1785197'); // Rig
break;
case 'Package':
// TODO: XlXi: Move these to config, as it could be different from prod in a testing environment. Also move these to their own assets (not loading from roblox).
array_push($arguments, 'https://www.roblox.com/asset/?id=1785197'); // Rig
array_push($arguments, '27113661;25251154'); // Custom Texture URLs (shirt and pands)
break;
case 'Place':
array_push($arguments, '0'); // TODO: XlXi: Universe IDs
break;
}
game:Load("http://gtoria.net/asset/?id=3529");
for _, Object in pairs(game:GetChildren())do
if Object:IsA("Tool") then
Object.Parent = workspace
end
end
-- format, width, height, sky, crop
return game:GetService("ThumbnailGenerator"):Click("OBJ", 840, 840, true, true)
TestScript;
$test = new SoapService('http://192.168.0.3:64989');
$result = $test->OpenJob(SoapService::MakeJobJSON(Str::uuid()->toString(), 120, 0, 0, sprintf('Render %s %d', $this->tracker->type, $this->tracker->target), $testScript));
$service = new SoapService('Thumbnail');
$result = $service->OpenJob(GridHelper::JobTemplate(
Str::uuid()->toString(), // Job ID
120, // Expiration
0, // Category ID
0, // Cores
sprintf('Render %s %d', $this->tracker->type, $this->tracker->target), // Script Name
$this->type, // Script
$arguments // Arguments
));
if(is_soap_fault($result))
$this->fail(sprintf('SOAP Fault: (faultcode: %s, faultstring: %s)', $result->faultcode, $result->faultstring));

View File

@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ArbiterSoapFault extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'function',
'code',
'message',
'job_arguments'
];
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('arbiter_soap_faults', function (Blueprint $table) {
$table->id();
$table->string('function');
$table->string('code');
$table->string('message');
$table->longText('job_arguments');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('arbiter_soap_faults');
}
};

View File

@ -43,5 +43,15 @@ class WebConfigurationSeeder extends Seeder
'name' => 'VersionCompatibilityHashes',
'value' => 'debughash' // hash1;hash2;hash3
]);
DynamicWebConfiguration::create([
'name' => 'GameArbiterIP',
'value' => '127.0.0.1'
]);
DynamicWebConfiguration::create([
'name' => 'ThumbnailArbiterIP',
'value' => '127.0.0.1'
]);
}
}

View File

@ -64,8 +64,7 @@
@yield('quick-admin')
@admin
<li class="nav-item">
{{-- TODO: XlXi: Make this use route() --}}
<a href="{{ url('/admin/arbiter-diag') }}" class="nav-link py-0">Arbiter Diag</a>
<a href="{{ route('admin.diag') }}" class="nav-link py-0">Arbiter Diag</a>
</li>
@endadmin
<li class="nav-item">

View File

@ -0,0 +1,35 @@
@extends('layouts.app')
@section('extra-headers')
<style>
.graphictoria-graph-card {
width: 300px!important;
height: 200px!important;
margin: 5px;
}
</style>
@endsection
@section('content')
<div class="container my-2">
<h4>Arbiter Diag <span class="text-muted">(RccServiceMonitor)</span></h4>
<div class="d-flex flex-wrap">
<x-minicard class="graphictoria-graph-card">
<x-slot name="title">
Active Jobs
</x-slot>
<x-slot name="body">
<div class="graphictoria-shop-overlay">
<x-loader />
</div>
</x-slot>
</x-minicard>
</div>
<h4 class="mt-2">Soap Exceptions</h4>
<div class="card p-3" id="gt-soap-exceptions" style="min-height:80px;">
<div class="graphictoria-shop-overlay">
<x-loader />
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,9 @@
@extends('layouts.app')
@section('title', 'Arbiter Diag')
@section('content')
<div class="container my-2">
<h4>Arbiter Diag <span class="text-muted">(RccServiceMonitor)</span></h4>
</div>
@endsection

View File

@ -23,6 +23,20 @@ Route::middleware('auth')->group(function () {
});
});
Route::group(['as' => 'admin.', 'prefix' => 'admin'], function() {
Route::middleware('roleset:moderator')->group(function () {
Route::get('/', 'AdminController@dashboard')->name('dashboard');
});
Route::middleware('roleset:administrator')->group(function () {
Route::get('/arbiter-diag/{arbiterType?}', 'AdminController@arbiterDiag')->name('diag');
});
Route::middleware('roleset:owner')->group(function () {
});
});
Route::group(['as' => 'auth.', 'namespace' => 'Auth'], function() {
Route::group(['as' => 'protection.', 'prefix' => 'request-blocked'], function() {
Route::get('/', 'DoubleSessionBlockController@index')->name('index');

View File

@ -1,3 +1,4 @@
*
!public/
!grid/
!.gitignore

1
web/storage/app/grid/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
!*

View File

@ -0,0 +1,800 @@
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://roblox.com/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" targetNamespace="http://roblox.com/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<wsdl:types>
<s:schema elementFormDefault="qualified" targetNamespace="http://roblox.com/">
<s:element name="HelloWorld">
<s:complexType />
</s:element>
<s:element name="HelloWorldResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="HelloWorldResult" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetVersion">
<s:complexType />
</s:element>
<s:element name="GetVersionResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="GetVersionResult" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetStatus">
<s:complexType />
</s:element>
<s:element name="GetStatusResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="GetStatusResult" type="tns:Status" />
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="Status">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="version" type="s:string" />
<s:element minOccurs="1" maxOccurs="1" name="environmentCount" type="s:int" />
</s:sequence>
</s:complexType>
<s:element name="OpenJob">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="job" type="tns:Job" />
<s:element minOccurs="0" maxOccurs="1" name="script" type="tns:ScriptExecution" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="OpenJobEx">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="job" type="tns:Job" />
<s:element minOccurs="0" maxOccurs="1" name="script" type="tns:ScriptExecution" />
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="Job">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="id" type="s:string" />
<s:element minOccurs="1" maxOccurs="1" name="expirationInSeconds" type="s:double" />
<s:element minOccurs="1" maxOccurs="1" name="category" type="s:int" />
<s:element minOccurs="1" maxOccurs="1" name="cores" type="s:double" />
</s:sequence>
</s:complexType>
<s:complexType name="ScriptExecution">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="name" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="script" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="arguments" type="tns:ArrayOfLuaValue" />
</s:sequence>
</s:complexType>
<s:complexType name="ArrayOfLuaValue">
<s:sequence>
<s:element minOccurs="0" maxOccurs="unbounded" name="LuaValue" nillable="true" type="tns:LuaValue" />
</s:sequence>
</s:complexType>
<s:complexType name="ArrayOfJob">
<s:sequence>
<s:element minOccurs="0" maxOccurs="unbounded" name="Job" nillable="true" type="tns:Job" />
</s:sequence>
</s:complexType>
<s:complexType name="LuaValue">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="type" type="tns:LuaType" />
<s:element minOccurs="0" maxOccurs="1" name="value" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="table" type="tns:ArrayOfLuaValue" />
</s:sequence>
</s:complexType>
<s:simpleType name="LuaType">
<s:restriction base="s:string">
<s:enumeration value="LUA_TNIL" />
<s:enumeration value="LUA_TBOOLEAN" />
<s:enumeration value="LUA_TNUMBER" />
<s:enumeration value="LUA_TSTRING" />
<s:enumeration value="LUA_TTABLE" />
</s:restriction>
</s:simpleType>
<s:element name="OpenJobResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="unbounded" name="OpenJobResult" type="tns:LuaValue"/>
</s:sequence>
</s:complexType>
</s:element>
<s:element name="OpenJobExResponse">
<s:complexType>
<s:sequence>
<s:element name="OpenJobExResult" type="tns:ArrayOfLuaValue"/>
</s:sequence>
</s:complexType>
</s:element>
<s:element name="RenewLease">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="jobID" type="s:string" />
<s:element minOccurs="1" maxOccurs="1" name="expirationInSeconds" type="s:double" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="RenewLeaseResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="RenewLeaseResult" type="s:double" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="Execute">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="jobID" type="s:string" />
<s:element minOccurs="1" maxOccurs="1" name="script" type="tns:ScriptExecution" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="ExecuteResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="unbounded" name="ExecuteResult" nillable="true" type="tns:LuaValue" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="ExecuteEx">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="jobID" type="s:string" />
<s:element minOccurs="1" maxOccurs="1" name="script" type="tns:ScriptExecution" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="ExecuteExResponse">
<s:complexType>
<s:sequence>
<s:element name="ExecuteExResult" type="tns:ArrayOfLuaValue"/>
</s:sequence>
</s:complexType>
</s:element>
<s:element name="CloseJob">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="jobID" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="CloseJobResponse">
<s:complexType />
</s:element>
<s:element name="BatchJob">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="job" type="tns:Job" />
<s:element minOccurs="1" maxOccurs="1" name="script" type="tns:ScriptExecution" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="BatchJobResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="unbounded" name="BatchJobResult" nillable="true" type="tns:LuaValue" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="BatchJobEx">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="job" type="tns:Job" />
<s:element minOccurs="1" maxOccurs="1" name="script" type="tns:ScriptExecution" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="BatchJobExResponse">
<s:complexType>
<s:sequence>
<s:element name="BatchJobExResult" type="tns:ArrayOfLuaValue"/>
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetExpiration">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="jobID" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetExpirationResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="GetExpirationResult" type="s:double" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetAllJobs">
<s:complexType />
</s:element>
<s:element name="GetAllJobsResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="unbounded" name="GetAllJobsResult" nillable="true" type="tns:Job" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetAllJobsEx">
<s:complexType />
</s:element>
<s:element name="GetAllJobsExResponse">
<s:complexType>
<s:sequence>
<s:element name="GetAllJobsExResult" type="tns:ArrayOfJob" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="CloseExpiredJobs">
<s:complexType />
</s:element>
<s:element name="CloseExpiredJobsResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="CloseExpiredJobsResult" type="s:int" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="CloseAllJobs">
<s:complexType />
</s:element>
<s:element name="CloseAllJobsResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="CloseAllJobsResult" type="s:int" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="Diag">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="type" type="s:int" />
<s:element minOccurs="0" maxOccurs="1" name="jobID" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="DiagResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="unbounded" name="DiagResult" nillable="true" type="tns:LuaValue" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="DiagEx">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="type" type="s:int" />
<s:element minOccurs="0" maxOccurs="1" name="jobID" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="DiagExResponse">
<s:complexType>
<s:sequence>
<s:element name="DiagExResult" type="tns:ArrayOfLuaValue" />
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</wsdl:types>
<wsdl:message name="HelloWorldSoapIn">
<wsdl:part name="parameters" element="tns:HelloWorld" />
</wsdl:message>
<wsdl:message name="HelloWorldSoapOut">
<wsdl:part name="parameters" element="tns:HelloWorldResponse" />
</wsdl:message>
<wsdl:message name="GetVersionSoapIn">
<wsdl:part name="parameters" element="tns:GetVersion" />
</wsdl:message>
<wsdl:message name="GetVersionSoapOut">
<wsdl:part name="parameters" element="tns:GetVersionResponse" />
</wsdl:message>
<wsdl:message name="GetStatusSoapIn">
<wsdl:part name="parameters" element="tns:GetStatus" />
</wsdl:message>
<wsdl:message name="GetStatusSoapOut">
<wsdl:part name="parameters" element="tns:GetStatusResponse" />
</wsdl:message>
<wsdl:message name="OpenJobSoapIn">
<wsdl:part name="parameters" element="tns:OpenJob" />
</wsdl:message>
<wsdl:message name="OpenJobSoapOut">
<wsdl:part name="parameters" element="tns:OpenJobResponse" />
</wsdl:message>
<wsdl:message name="OpenJobExSoapIn">
<wsdl:part name="parameters" element="tns:OpenJobEx" />
</wsdl:message>
<wsdl:message name="OpenJobExSoapOut">
<wsdl:part name="parameters" element="tns:OpenJobExResponse" />
</wsdl:message>
<wsdl:message name="RenewLeaseSoapIn">
<wsdl:part name="parameters" element="tns:RenewLease" />
</wsdl:message>
<wsdl:message name="RenewLeaseSoapOut">
<wsdl:part name="parameters" element="tns:RenewLeaseResponse" />
</wsdl:message>
<wsdl:message name="ExecuteSoapIn">
<wsdl:part name="parameters" element="tns:Execute" />
</wsdl:message>
<wsdl:message name="ExecuteSoapOut">
<wsdl:part name="parameters" element="tns:ExecuteResponse" />
</wsdl:message>
<wsdl:message name="ExecuteExSoapIn">
<wsdl:part name="parameters" element="tns:ExecuteEx" />
</wsdl:message>
<wsdl:message name="ExecuteExSoapOut">
<wsdl:part name="parameters" element="tns:ExecuteExResponse" />
</wsdl:message>
<wsdl:message name="CloseJobSoapIn">
<wsdl:part name="parameters" element="tns:CloseJob" />
</wsdl:message>
<wsdl:message name="CloseJobSoapOut">
<wsdl:part name="parameters" element="tns:CloseJobResponse" />
</wsdl:message>
<wsdl:message name="BatchJobSoapIn">
<wsdl:part name="parameters" element="tns:BatchJob" />
</wsdl:message>
<wsdl:message name="BatchJobSoapOut">
<wsdl:part name="parameters" element="tns:BatchJobResponse" />
</wsdl:message>
<wsdl:message name="BatchJobExSoapIn">
<wsdl:part name="parameters" element="tns:BatchJobEx" />
</wsdl:message>
<wsdl:message name="BatchJobExSoapOut">
<wsdl:part name="parameters" element="tns:BatchJobExResponse" />
</wsdl:message>
<wsdl:message name="GetExpirationSoapIn">
<wsdl:part name="parameters" element="tns:GetExpiration" />
</wsdl:message>
<wsdl:message name="GetExpirationSoapOut">
<wsdl:part name="parameters" element="tns:GetExpirationResponse" />
</wsdl:message>
<wsdl:message name="GetAllJobsSoapIn">
<wsdl:part name="parameters" element="tns:GetAllJobs" />
</wsdl:message>
<wsdl:message name="GetAllJobsSoapOut">
<wsdl:part name="parameters" element="tns:GetAllJobsResponse" />
</wsdl:message>
<wsdl:message name="GetAllJobsExSoapIn">
<wsdl:part name="parameters" element="tns:GetAllJobsEx" />
</wsdl:message>
<wsdl:message name="GetAllJobsExSoapOut">
<wsdl:part name="parameters" element="tns:GetAllJobsExResponse" />
</wsdl:message>
<wsdl:message name="CloseExpiredJobsSoapIn">
<wsdl:part name="parameters" element="tns:CloseExpiredJobs" />
</wsdl:message>
<wsdl:message name="CloseExpiredJobsSoapOut">
<wsdl:part name="parameters" element="tns:CloseExpiredJobsResponse" />
</wsdl:message>
<wsdl:message name="CloseAllJobsSoapIn">
<wsdl:part name="parameters" element="tns:CloseAllJobs" />
</wsdl:message>
<wsdl:message name="CloseAllJobsSoapOut">
<wsdl:part name="parameters" element="tns:CloseAllJobsResponse" />
</wsdl:message>
<wsdl:message name="DiagSoapIn">
<wsdl:part name="parameters" element="tns:Diag" />
</wsdl:message>
<wsdl:message name="DiagSoapOut">
<wsdl:part name="parameters" element="tns:DiagResponse" />
</wsdl:message>
<wsdl:message name="DiagExSoapIn">
<wsdl:part name="parameters" element="tns:DiagEx" />
</wsdl:message>
<wsdl:message name="DiagExSoapOut">
<wsdl:part name="parameters" element="tns:DiagExResponse" />
</wsdl:message>
<wsdl:portType name="RCCServiceSoap">
<wsdl:operation name="HelloWorld">
<wsdl:input message="tns:HelloWorldSoapIn" />
<wsdl:output message="tns:HelloWorldSoapOut" />
</wsdl:operation>
<wsdl:operation name="GetVersion">
<wsdl:input message="tns:GetVersionSoapIn" />
<wsdl:output message="tns:GetVersionSoapOut" />
</wsdl:operation>
<wsdl:operation name="GetStatus">
<wsdl:input message="tns:GetStatusSoapIn" />
<wsdl:output message="tns:GetStatusSoapOut" />
</wsdl:operation>
<wsdl:operation name="OpenJob">
<wsdl:input message="tns:OpenJobSoapIn" />
<wsdl:output message="tns:OpenJobSoapOut" />
</wsdl:operation>
<wsdl:operation name="OpenJobEx">
<wsdl:input message="tns:OpenJobExSoapIn" />
<wsdl:output message="tns:OpenJobExSoapOut" />
</wsdl:operation>
<wsdl:operation name="RenewLease">
<wsdl:input message="tns:RenewLeaseSoapIn" />
<wsdl:output message="tns:RenewLeaseSoapOut" />
</wsdl:operation>
<wsdl:operation name="Execute">
<wsdl:input message="tns:ExecuteSoapIn" />
<wsdl:output message="tns:ExecuteSoapOut" />
</wsdl:operation>
<wsdl:operation name="ExecuteEx">
<wsdl:input message="tns:ExecuteExSoapIn" />
<wsdl:output message="tns:ExecuteExSoapOut" />
</wsdl:operation>
<wsdl:operation name="CloseJob">
<wsdl:input message="tns:CloseJobSoapIn" />
<wsdl:output message="tns:CloseJobSoapOut" />
</wsdl:operation>
<wsdl:operation name="BatchJob">
<wsdl:input message="tns:BatchJobSoapIn" />
<wsdl:output message="tns:BatchJobSoapOut" />
</wsdl:operation>
<wsdl:operation name="BatchJobEx">
<wsdl:input message="tns:BatchJobExSoapIn" />
<wsdl:output message="tns:BatchJobExSoapOut" />
</wsdl:operation>
<wsdl:operation name="GetExpiration">
<wsdl:input message="tns:GetExpirationSoapIn" />
<wsdl:output message="tns:GetExpirationSoapOut" />
</wsdl:operation>
<wsdl:operation name="GetAllJobs">
<wsdl:input message="tns:GetAllJobsSoapIn" />
<wsdl:output message="tns:GetAllJobsSoapOut" />
</wsdl:operation>
<wsdl:operation name="GetAllJobsEx">
<wsdl:input message="tns:GetAllJobsExSoapIn" />
<wsdl:output message="tns:GetAllJobsExSoapOut" />
</wsdl:operation>
<wsdl:operation name="CloseExpiredJobs">
<wsdl:input message="tns:CloseExpiredJobsSoapIn" />
<wsdl:output message="tns:CloseExpiredJobsSoapOut" />
</wsdl:operation>
<wsdl:operation name="CloseAllJobs">
<wsdl:input message="tns:CloseAllJobsSoapIn" />
<wsdl:output message="tns:CloseAllJobsSoapOut" />
</wsdl:operation>
<wsdl:operation name="Diag">
<wsdl:input message="tns:DiagSoapIn" />
<wsdl:output message="tns:DiagSoapOut" />
</wsdl:operation>
<wsdl:operation name="DiagEx">
<wsdl:input message="tns:DiagExSoapIn" />
<wsdl:output message="tns:DiagExSoapOut" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="RCCServiceSoap" type="tns:RCCServiceSoap">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="HelloWorld">
<soap:operation soapAction="http://roblox.com/HelloWorld" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetVersion">
<soap:operation soapAction="http://roblox.com/GetVersion" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetStatus">
<soap:operation soapAction="http://roblox.com/GetStatus" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="OpenJob">
<soap:operation soapAction="http://roblox.com/OpenJob" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="OpenJobEx">
<soap:operation soapAction="http://roblox.com/OpenJobEx" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="RenewLease">
<soap:operation soapAction="http://roblox.com/RenewLease" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Execute">
<soap:operation soapAction="http://roblox.com/Execute" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="ExecuteEx">
<soap:operation soapAction="http://roblox.com/ExecuteEx" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="CloseJob">
<soap:operation soapAction="http://roblox.com/CloseJob" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="BatchJob">
<soap:operation soapAction="http://roblox.com/BatchJob" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="BatchJobEx">
<soap:operation soapAction="http://roblox.com/BatchJobEx" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetExpiration">
<soap:operation soapAction="http://roblox.com/GetExpiration" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetAllJobs">
<soap:operation soapAction="http://roblox.com/GetAllJobs" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetAllJobsEx">
<soap:operation soapAction="http://roblox.com/GetAllJobsEx" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="CloseExpiredJobs">
<soap:operation soapAction="http://roblox.com/CloseExpiredJobs" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="CloseAllJobs">
<soap:operation soapAction="http://roblox.com/CloseAllJobs" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Diag">
<soap:operation soapAction="http://roblox.com/Diag" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="DiagEx">
<soap:operation soapAction="http://roblox.com/DiagEx" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:binding name="RCCServiceSoap12" type="tns:RCCServiceSoap">
<soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="HelloWorld">
<soap12:operation soapAction="http://roblox.com/HelloWorld" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetVersion">
<soap12:operation soapAction="http://roblox.com/GetVersion" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetStatus">
<soap12:operation soapAction="http://roblox.com/GetStatus" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="OpenJob">
<soap12:operation soapAction="http://roblox.com/OpenJob" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="OpenJobEx">
<soap12:operation soapAction="http://roblox.com/OpenJobEx" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="RenewLease">
<soap12:operation soapAction="http://roblox.com/RenewLease" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Execute">
<soap12:operation soapAction="http://roblox.com/Execute" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="ExecuteEx">
<soap12:operation soapAction="http://roblox.com/ExecuteEx" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="CloseJob">
<soap12:operation soapAction="http://roblox.com/CloseJob" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="BatchJob">
<soap12:operation soapAction="http://roblox.com/BatchJob" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="BatchJobEx">
<soap12:operation soapAction="http://roblox.com/BatchJobEx" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetExpiration">
<soap12:operation soapAction="http://roblox.com/GetExpiration" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetAllJobs">
<soap12:operation soapAction="http://roblox.com/GetAllJobs" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetAllJobsEx">
<soap12:operation soapAction="http://roblox.com/GetAllJobsEx" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="CloseExpiredJobs">
<soap12:operation soapAction="http://roblox.com/CloseExpiredJobs" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="CloseAllJobs">
<soap12:operation soapAction="http://roblox.com/CloseAllJobs" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Diag">
<soap12:operation soapAction="http://roblox.com/Diag" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="DiagEx">
<soap12:operation soapAction="http://roblox.com/DiagEx" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<service name="RCCServiceSoap">
<port name="RCCServiceSoapPort" binding="tns:RCCServiceSoap">
<soap:address location="127.0.0.1:64989"/>
</port>
</service>
</wsdl:definitions>

View File

@ -0,0 +1,26 @@
local characterAppearanceUrl, baseUrl, fileExtension, x, y = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ContentProvider"):SetThreadPool(16)
game:GetService("ScriptContext").ScriptsDisabled = true
settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4;
local Lighting = game:GetService("Lighting")
Lighting.ClockTime = 13
Lighting.GeographicLatitude = -5
local player = game:GetService("Players"):CreateLocalPlayer(0)
player.CharacterAppearance = characterAppearanceUrl
player:LoadCharacter(false)
-- Raise up the character's arm if they have gear.
if player.Character then
for _, child in pairs(player.Character:GetChildren()) do
if child:IsA("Tool") then
player.Character.Torso["Right Shoulder"].CurrentAngle = math.rad(90)
break
end
end
end
return game:GetService("ThumbnailGenerator"):Click(fileExtension, x, y, --[[hideSky = ]] true)

View File

@ -0,0 +1,20 @@
local assetUrl, fileExtension, x, y, baseUrl, rigUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ContentProvider"):SetThreadPool(16)
game:GetService("ScriptContext").ScriptsDisabled = true
settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4;
local Lighting = game:GetService("Lighting")
Lighting.ClockTime = 13
Lighting.GeographicLatitude = -5
local mannequin = game:GetObjects(rigUrl)[1]
mannequin.Humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None
mannequin.Parent = workspace
for _, object in pairs(game:GetObjects(assetUrl)) do
object.Parent = mannequin
end
return game:GetService("ThumbnailGenerator"):Click(fileExtension, x, y, --[[hideSky = ]] true)

View File

@ -0,0 +1,50 @@
local baseUrl, characterAppearanceUrl, fileExtension, x, y, quadratic, baseHatZoom, maxHatZoom, cameraOffsetX, cameraOffsetY = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ContentProvider"):SetThreadPool(16)
game:GetService("ScriptContext").ScriptsDisabled = true
settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4;
local Lighting = game:GetService("Lighting")
Lighting.ClockTime = 13
Lighting.GeographicLatitude = -5
local player = game:GetService("Players"):CreateLocalPlayer(0)
player.CharacterAppearance = characterAppearanceUrl
player:LoadCharacter(false)
local maxDimension = 0
if player.Character then
-- Remove gear
for _, child in pairs(player.Character:GetChildren()) do
if child:IsA("Tool") then
child:Destroy()
elseif child:IsA("Accoutrement") then
local size = child.Handle.Size / 2 + child.Handle.Position - player.Character.Head.Position
local xy = Vector2.new(size.x, size.y)
if xy.magnitude > maxDimension then
maxDimension = xy.magnitude
end
end
end
-- Setup Camera
local maxHatOffset = 0.5 -- Maximum amount to move camera upward to accomodate large hats
maxDimension = math.min(1, maxDimension / 3) -- Confine maxdimension to specific bounds
if quadratic then
maxDimension = maxDimension * maxDimension -- Zoom out on quadratic interpolation
end
local viewOffset = player.Character.Head.CFrame * CFrame.new(cameraOffsetX, cameraOffsetY + maxHatOffset * maxDimension, 0.1) -- View vector offset from head
local positionOffset = player.Character.Head.CFrame + (CFrame.Angles(0, -math.pi / 16, 0).lookVector.unit * 3) -- Position vector offset from head
local camera = Instance.new("Camera", player.Character)
camera.Name = "ThumbnailCamera"
camera.CameraType = Enum.CameraType.Scriptable
camera.CoordinateFrame = CFrame.new(positionOffset.p, viewOffset.p)
camera.FieldOfView = baseHatZoom + (maxHatZoom - baseHatZoom) * maxDimension
end
return game:GetService("ThumbnailGenerator"):Click(fileExtension, x, y, --[[hideSky = ]] true)

View File

@ -0,0 +1,22 @@
local assetUrl, fileExtension, x, y, baseUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ContentProvider"):SetThreadPool(16)
game:GetService("ScriptContext").ScriptsDisabled = true
settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4;
local decal = game:GetObjects(assetUrl)[1]
local thumbnailGenerator = game:GetService("ThumbnailGenerator")
local image, requestedUrls
local success = pcall(function()
image, requestedUrls = thumbnailGenerator:ClickTexture(decal.Texture, fileExtension, x, y)
end)
if success then
return image, requestedUrls
end
-- if we fail return the hourglass, since we're probably in moderation.
return "/9j/4AAQSkZJRgABAQEASABIAAD//gATQ3JlYXRlZCB3aXRoIEdJTVD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAABAAEDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAX8P/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQABBQJ//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwF//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwF//8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQAGPwJ//8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQABPyF//9oADAMBAAIAAwAAABAf/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPxB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPxB//8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQABPxB//9k=", {decal.Texture}

View File

@ -0,0 +1,16 @@
local assetUrl, fileExtension, x, y, baseUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ContentProvider"):SetThreadPool(16)
game:GetService("ScriptContext").ScriptsDisabled = true
settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4;
local Lighting = game:GetService("Lighting")
Lighting.ClockTime = 13
Lighting.GeographicLatitude = -5
for _, object in pairs(game:GetObjects(assetUrl)) do
object.Parent = workspace
end
return game:GetService("ThumbnailGenerator"):Click(fileExtension, x, y, --[[hideSky = ]] true, --[[crop = ]] true)

View File

@ -0,0 +1,16 @@
local assetUrl, fileExtension, x, y, baseUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ContentProvider"):SetThreadPool(16)
game:GetService("ScriptContext").ScriptsDisabled = true
settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4;
local Lighting = game:GetService("Lighting")
Lighting.ClockTime = 13
Lighting.GeographicLatitude = -5
for _, object in pairs(game:GetObjects(assetUrl)) do
object.Parent = workspace
end
return game:GetService("ThumbnailGenerator"):Click(fileExtension, x, y, --[[hideSky = ]] true, --[[crop = ]] true)

View File

@ -0,0 +1,31 @@
local assetUrl, fileExtension, x, y, baseUrl, rigUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ContentProvider"):SetThreadPool(16)
game:GetService("ScriptContext").ScriptsDisabled = true
settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4;
local Lighting = game:GetService("Lighting")
Lighting.ClockTime = 13
Lighting.GeographicLatitude = -5
local mannequin = game:GetObjects(rigUrl)[1]
mannequin.Humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None
mannequin.Parent = workspace
local object = game:GetObjects(assetUrl)[1]
mannequin.Head.BrickColor = BrickColor.Gray()
if mannequin.Head:FindFirstChild("Mesh") then
mannequin.Head.Mesh:Destroy()
end
object.Parent = mannequin.Head
for _, child in pairs(mannequin:GetChildren()) do
if child:IsA("BasePart") and child.Name ~= "Head" then
child:Destroy()
end
end
return game:GetService("ThumbnailGenerator"):Click(fileExtension, x, y, --[[hideSky = ]] true)

View File

@ -0,0 +1,19 @@
local assetUrl, fileExtension, x, y, baseUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ContentProvider"):SetThreadPool(16)
game:GetService("ScriptContext").ScriptsDisabled = true
settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4;
local Lighting = game:GetService("Lighting")
Lighting.ClockTime = 13
Lighting.GeographicLatitude = -5
local part = Instance.new("Part")
part.Parent = workspace
local specialMesh = Instance.new("SpecialMesh")
specialMesh.MeshId = assetUrl
specialMesh.Parent = part
return game:GetService("ThumbnailGenerator"):Click(fileExtension, x, y, --[[hideSky = ]] true, --[[crop = ]] true)

View File

@ -0,0 +1,36 @@
local assetUrl, fileExtension, x, y, baseUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ContentProvider"):SetThreadPool(16)
game:GetService("ScriptContext").ScriptsDisabled = true
settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4;
local Lighting = game:GetService("Lighting")
Lighting.ClockTime = 13
Lighting.GeographicLatitude = -5
local ThumbnailGenerator = game:GetService("ThumbnailGenerator")
for _, object in pairs(game:GetObjects(assetUrl)) do
if object:IsA("Sky") then
local resultValues = nil
local success = pcall(function() resultValues = {ThumbnailGenerator:ClickTexture(object.SkyboxFt, fileExtension, x, y)} end)
if success then
return unpack(resultValues)
else
object.Parent = game:GetService("Lighting")
return ThumbnailGenerator:Click(fileExtension, x, y, --[[hideSky = ]] false)
end
elseif object:IsA("LuaSourceContainer") then
return ThumbnailGenerator:ClickTexture(baseUrl.. "Thumbs/Script.png", fileExtension, x, y)
elseif object:IsA("SpecialMesh") then
local part = Instance.new("Part")
part.Parent = workspace
object.Parent = part
return ThumbnailGenerator:Click(fileExtension, x, y, --[[hideSky = ]] true)
else
pcall(function() object.Parent = workspace end)
end
end
return ThumbnailGenerator:Click(fileExtension, x, y, --[[hideSky = ]] true)

View File

@ -0,0 +1,131 @@
local assetUrls, fileExtension, x, y, baseUrl, rigUrl, customTextureUrls = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ContentProvider"):SetThreadPool(16)
game:GetService("ScriptContext").ScriptsDisabled = true
settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4;
local Lighting = game:GetService("Lighting")
Lighting.ClockTime = 13
Lighting.GeographicLatitude = -5
function split(str, delim)
local results = {}
local lastMatchEnd = 0
local matchStart, matchEnd = string.find(str, delim, --[[init = ]] 1, --[[plain = ]] true)
while matchStart and matchEnd do
if matchStart - lastMatchEnd > 1 then
table.insert(results, string.sub(str, lastMatchEnd + 1, matchStart - 1))
end
lastMatchEnd = matchEnd
matchStart, matchEnd = string.find(str, delim, --[[init = ]] lastMatchEnd + 1, --[[plain = ]] true)
end
if string.len(str) - lastMatchEnd > 1 then
table.insert(results, string.sub(str, lastMatchEnd + 1))
end
return results
end
local mannequin = game:GetObjects(rigUrl)[1]
mannequin.Humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None
mannequin.Parent = workspace
local accoutrements = {}
local assetUrlsList = split(assetUrls, ";")
for _, assetUrl in pairs(assetUrlsList) do
local currObject = game:GetObjects(assetUrl)[1]
if currObject:IsA("Tool") then
annequin.Torso["Right Shoulder"].CurrentAngle = math.rad(90)
currObject.Parent = mannequin
elseif currObject:IsA("DataModelMesh") then
local headMesh = mannequin.Head:FindFirstChild("Mesh")
if headMesh then
headMesh:Destroy()
end
currObject.Parent = mannequin.Head
elseif currObject:IsA("Decal") then
local face = mannequin.Head:FindFirstChild("face")
if face then
face:Destroy()
end
currObject.Parent = mannequin.Head
elseif currObject:IsA("Accoutrement") then
table.insert(accoutrements, currObject)
else
currObject.Parent = mannequin
end
end
local textureUrls = split(customTextureUrls, ";")
for _, url in pairs(textureUrls) do
local obj = game:GetObjects(url)[1]
if obj:IsA("Shirt") then
-- Don't add a texture Shirt if package already has a Shirt
if not mannequin:FindFirstChildOfClass("Shirt") then
obj.Parent = mannequin
end
elseif obj:IsA("Pants") then
-- Don't add a texture Pants if package already has a Pants
if not mannequin:FindFirstChildOfClass("Pants") then
obj.Parent = mannequin
end
else
obj.Parent = mannequin
end
end
function findFirstMatchingAttachment(model, name)
for _, child in pairs(model:GetChildren()) do
if child:IsA("Attachment") and child.Name == name then
return child
elseif not child:IsA("Accoutrement") and not child:IsA("Tool") then
local foundAttachment = findFirstMatchingAttachment(child, name)
if foundAttachment then
return foundAttachment
end
end
end
end
for _, accoutrement in pairs(accoutrements) do
local handle = accoutrement:FindFirstChild("Handle")
if handle then
local accoutrementAttachment = handle:FindFirstChildOfClass("Attachment")
local characterAttachment = nil
if accoutrementAttachment then
characterAttachment = findFirstMatchingAttachment(mannequin, accoutrementAttachment.Name)
end
local attachmentPart = nil
if characterAttachment then
attachmentPart = characterAttachment.Parent
else
attachmentPart = mannequin:FindFirstChild("Head")
end
local attachmentCFrame = nil
if characterAttachment then
attachmentCFrame = characterAttachment.CFrame
else
attachmentCFrame = CFrame.new(0, 0.5, 0)
end
local hatCFrame = nil
if accoutrementAttachment then
hatCFrame = accoutrementAttachment.CFrame
else
hatCFrame = accoutrement.AttachmentPoint
end
handle.CFrame = attachmentPart.CFrame * attachmentCFrame * hatCFrame:inverse()
handle.Anchored = true
handle.Parent = mannequin
end
end
return game:GetService("ThumbnailGenerator"):Click(fileExtension, x, y, --[[hideSky = ]] true)

View File

@ -0,0 +1,19 @@
local assetUrl, fileExtension, x, y, baseUrl, rigUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ContentProvider"):SetThreadPool(16)
game:GetService("ScriptContext").ScriptsDisabled = true
settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4;
local Lighting = game:GetService("Lighting")
Lighting.ClockTime = 13
Lighting.GeographicLatitude = -5
local mannequin = game:GetObjects(rigUrl)[1]
mannequin.Humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None
mannequin.Parent = workspace
local pants = game:GetObjects(assetUrl)[1]
pants.Parent = mannequin
return game:GetService("ThumbnailGenerator"):Click(fileExtension, x, y, --[[hideSky = ]] true)

View File

@ -0,0 +1,19 @@
local assetUrl, fileExtension, x, y, baseUrl, universeId = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ContentProvider"):SetThreadPool(16)
settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4;
if universeId ~= nil then
pcall(function() game:SetUniverseId(universeId) end)
end
game:GetService("ScriptContext").ScriptsDisabled = true
game:GetService("StarterGui").ShowDevelopmentGui = false
game:Load(assetUrl)
-- Do this after again loading the place file to ensure that these values aren't changed when the place file is loaded.
game:GetService("ScriptContext").ScriptsDisabled = true
game:GetService("StarterGui").ShowDevelopmentGui = false
return game:GetService("ThumbnailGenerator"):Click(fileExtension, x, y, --[[hideSky = ]] false)

View File

@ -0,0 +1,19 @@
local assetUrl, fileExtension, x, y, baseUrl, rigUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ContentProvider"):SetThreadPool(16)
game:GetService("ScriptContext").ScriptsDisabled = true
settings()["Task Scheduler"].ThreadPoolConfig = Enum.ThreadPoolConfig.PerCore4;
local Lighting = game:GetService("Lighting")
Lighting.ClockTime = 13
Lighting.GeographicLatitude = -5
local mannequin = game:GetObjects(rigUrl)[1]
mannequin.Humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None
mannequin.Parent = workspace
local shirt = game:GetObjects(assetUrl)[1]
shirt.Parent = mannequin
return game:GetService("ThumbnailGenerator"):Click(fileExtension, x, y, --[[hideSky = ]] true)