This commit is contained in:
lx
2026-07-06 14:01:11 -04:00
commit c4f97d729d
16468 changed files with 935321 additions and 0 deletions
@@ -0,0 +1,320 @@
JSONSettings = ...
local placeId = JSONSettings["PlaceId"]
local universeId = JSONSettings["UniverseId"]
local matchmakingContextId = JSONSettings["MatchmakingContextId"]
local gameCode = JSONSettings["GameCode"]
local baseUrl = JSONSettings["BaseUrl"]
local gameId = JSONSettings["GameId"]
local machineAddress = JSONSettings["MachineAddress"]
local gsmInterval = JSONSettings["GsmInterval"]
local maxPlayers = JSONSettings["MaxPlayers"]
local maxGameInstances = JSONSettings["MaxGameInstances"]
local apiKey = JSONSettings["ApiKey"]
local preferredPlayerCapacity = JSONSettings["PreferredPlayerCapacity"]
local placeVisitAccessKey = JSONSettings["PlaceVisitAccessKey"]
local access = true
local isCloudEdit = matchmakingContextId == 3
local assetGameUrl = "https://assetgame." .. baseUrl
-- Monitor Game Status LUA Script
-- Reports game stats to a web handler on regular intervals
-- Assumes the following local variables have been defined:
-- url - root url for the environment (e.g. www.roblox.com)
-- placeId - place being run
-- maxPlayers - maximum number of players allowed in this game
-- preferredPlayerCapacity - number of player slots reserved for social matchmaking
-- machineAddress - IP address of the server
-- gsmInterval - send the message no matter what on this interval
-- baseUrl - base url to build other urls
-- isCloudEdit - if the current server context is cloudEdit
-- -----------------------------------------------------------
startTime = tick()
print("yo yo does this work??")
networkServer = game:GetService("NetworkServer")
playersService = game:GetService("Players")
httpService = game:GetService("HttpService")
pcall(function() playersService.MaxPlayers = maxPlayers end)
pcall(function() playersService.MaxPlayersInternal = maxPlayers end)
pcall(function() playersService.PreferredPlayersInternal = preferredPlayerCapacity end)
gsmUrl = "https://games.api." .. baseUrl
gameInstancesApiUrl = "https://gameinstances.api." .. baseUrl
apiProxyUrl = "https://api." .. baseUrl
playerJoinTimes = {}
local RCCKickDupeExists, RCCKickDupeEnabled = pcall(function()
return settings():GetFFlag("RCCKickDuplicatePlayersOnJoin")
end)
local kickDuplicatePlayersRCC = RCCKickDupeExists and RCCKickDupeEnabled and not isCloudEdit;
function UrlEncode(s)
s = string.gsub(s, "([&=+%c])", function (c)
return string.format("%%%02X", string.byte(c))
end)
s = string.gsub(s, " ", "+")
return s
end
function Split(str, pat)
local t = {} -- NOTE: use {n = 0} in Lua-5.0
local fpat = "(.-)" .. pat
local last_end = 1
local s, e, cap = str:find(fpat, 1)
while s do
if s ~= 1 or cap ~= "" then
table.insert(t,cap)
end
last_end = e + 1
s, e, cap = str:find(fpat, last_end)
end
if last_end <= #str then
cap = str:sub(last_end)
table.insert(t, cap)
end
return t
end
function CalculateAveragePing()
local totalPing = 0
local replicatorCount = 0
local averagePing = 0
local status, err = pcall(function()
for _, r in ipairs(stats().Network:GetChildren()) do
if r.Name ~= "Packets Thread" then
r:GetValue() -- hax
totalPing = totalPing + r.Ping:GetValue()
replicatorCount = replicatorCount + 1
end
end
if replicatorCount > 0 then
averagePing = totalPing / replicatorCount
end
end)
if (not status) then
PrintDebugMessage("CalculateAveragePing error = " .. err)
end
return averagePing
end
function PrintDebugMessage(message)
if message then
-- print ("!GameServiceMonitor: " .. message)
-- game:HttpPost(gsmUrl .. "/v1.0/LogLuaMessage?&apiKey=" .. apiKey .. "&text=" .. UrlEncode(message), "")
end
end
function PrintErrorMessage(message)
if message then
print ("!GameServiceMonitor: " .. message)
-- game:HttpPost(gsmUrl .. "/v1.0/LogLuaMessage?&apiKey=" .. apiKey .. "&text=" .. UrlEncode(message), "")
end
end
function UpdatePresence(player, isDisconnect)
local fullUrl = apiProxyUrl .. "/presence/"
local queryParams = "?visitorId=" .. player.userId
if isDisconnect then
fullUrl = fullUrl .. "register-absence"
else
fullUrl = fullUrl .. "register-game-presence"
queryParams = queryParams .. "&placeId=" .. placeId .. "&gameId=" .. gameId .. "&locationType=" .. ((isCloudEdit or matchmakingContextId == 4) and "CloudEdit" or "Game")
end
fullUrl = fullUrl .. queryParams
PrintDebugMessage("Calling Api Proxy to update presence. URL: " .. fullUrl)
game:HttpPost(fullUrl, "")
return true
end
function GetPlayerPostData()
local sessionArray = {}
for _, player in ipairs(playersService:GetPlayers()) do
local t =
{
UserId = player.userId,
IsVr = player.VRDevice ~= "",
GameTimeWhenJoined = playerJoinTimes[player.userId],
GameSessionId = player:GetGameSessionID()
}
table.insert(sessionArray, t)
end
return httpService:JSONEncode({GameSessions = sessionArray})
end
function SendMessageToGamesApi(source, player)
local postDataJson = ""
local playerCSV = ""
postDataJson = GetPlayerPostData()
local w = stats().Workspace
local gameTime = tick() - startTime
local fullUrl = ""
fullUrl = gsmUrl .. "/v2.0/Refresh/?apiKey=" .. apiKey
fullUrl = fullUrl .. "&gameId=" .. gameId .."&placeId=" .. placeId .."&gameCapacity=" .. maxPlayers
fullUrl = fullUrl .. "&maximumGameInstances=" .. maxGameInstances .."&ipAddress=" .. machineAddress
fullUrl = fullUrl .. "&port=" .. networkServer.Port .."&clientCount=" .. networkServer:GetClientCount()
fullUrl = fullUrl .. "&gameTime=" .. gameTime
fullUrl = fullUrl .. "&preferredPlayerCapacity=" .. preferredPlayerCapacity
fullUrl = fullUrl .. "&eventSource=" .. source
if player ~= nil then
fullUrl = fullUrl .. "&originatingPlayerId=" .. player.userId
end
fullUrl = fullUrl .. "&gameCode="
if gameCode ~= nil then
fullUrl = fullUrl .. gameCode
end
fullUrl = fullUrl .. "&matchmakingContextId=" .. matchmakingContextId
fullUrl = fullUrl .. "&isCloudEdit=" .. ((isCloudEdit or matchmakingContextId == 4) and "true" or "false")
fullUrl = fullUrl .. "&rccVersion=" .. version()
PrintDebugMessage("Calling Games API. Source: " .. source .. ", URL: " .. fullUrl .. " Post data (JSON}:" .. postDataJson)
game:HttpPost(fullUrl, postDataJson, false, "application/json")
return true
end
function SendMessageToGameInstancesApi(source)
local postDataJson = ""
postDataJson = GetPlayerPostData()
local w = stats().Workspace
local gameTime = tick() - startTime
local averagePing = CalculateAveragePing()
local fullUrl = ""
fullUrl = gameInstancesApiUrl .. "/v2/CreateOrUpdate/?apiKey=" .. apiKey
fullUrl = fullUrl .. "&gameId=" .. gameId .."&placeId=" .. placeId .. "&gameCapacity=" .. maxPlayers
fullUrl = fullUrl .. "&maximumGameInstances=" .. maxGameInstances .. "&serverIpAddress=" .. machineAddress
fullUrl = fullUrl .. "&serverPort=" .. networkServer.Port .. "&fps=" .. w.FPS:GetValue()
fullUrl = fullUrl .. "&heartbeatRate=" .. w.Heartbeat:GetValue()
fullUrl = fullUrl .. "&ping=" .. averagePing .. "&gameTime=" .. gameTime
fullUrl = fullUrl .. "&universeId=" .. universeId
fullUrl = fullUrl .. "&gameCode="
if gameCode ~= nil then
fullUrl = fullUrl .. gameCode
end
fullUrl = fullUrl .. "&matchmakingContextId=" .. matchmakingContextId
PrintDebugMessage("Calling Game Instances API. Source: " .. source .. ", URL: " .. fullUrl .. " Post data (json): " .. postDataJson)
game:HttpPost(fullUrl, postDataJson, false, "application/json")
return true
end
-- Send updates to Games API and Game Instances API every gsmInterval seconds
delay(0, function()
while networkServer.Port == 0 do
wait(1)
end
while true do
-- always send on gsmInterval, we also send on events when player join and leave.
pcall(function() return SendMessageToGamesApi("HeartBeat", nil) end)
pcall(function() return SendMessageToGameInstancesApi("HeartBeat") end)
wait(gsmInterval)
end
end
)
-- Events that make HTTP calls back to endpoints tracking player activity
local function onPlayerConnectingReportClientPresence(player)
if assetGameUrl and access and placeId and player and player.userId then
local didTeleportIn = "False"
if player.TeleportedIn then didTeleportIn = "True" end
game:HttpGet(assetGameUrl .. "/Game/ClientPresence.ashx?action=connect&PlaceID=" .. placeId .. "&UserID=" .. player.userId)
if not isCloudEdit then
game:HttpPost(assetGameUrl .. "/Game/PlaceVisit.ashx?UserID=" .. player.userId .. "&AssociatedPlaceID=" .. placeId .. "&placeVisitAccessKey=" .. placeVisitAccessKey .. "&IsTeleport=" .. didTeleportIn, "")
end
end
end
local function onPlayerConnectingReportClientPresence2(player)
playerJoinTimes[player.userId] = tick() - startTime
-- Games API
local success, err = pcall(function() return SendMessageToGamesApi("PlayerAdded", player) end)
if (not success) then
PrintErrorMessage("playersService.PlayerAdded error updating games api = " .. err)
end
-- Game Instances API
success, err = pcall(function() return SendMessageToGameInstancesApi("PlayerAdded") end )
if (not success) then
PrintErrorMessage("playersService.PlayerAdded error updating game instances api = " .. err)
end
-- Api Proxy - Presence
success, err = pcall(function() return UpdatePresence(player, false) end)
if (not success) then
PrintErrorMessage("playersService.PlayerAdded error updating presence = " .. err)
end
end
local function onPlayerDisconnectingReportClientPresence(player)
local isTeleportingOut = "False"
if player.Teleported then isTeleportingOut = "True" end
if assetGameUrl and access and placeId and player and player.userId then
game:HttpGet(assetGameUrl .. "/Game/ClientPresence.ashx?action=disconnect&PlaceID=" .. placeId .. "&UserID=" .. player.userId .. "&IsTeleport=" .. isTeleportingOut)
end
end
local function onPlayerDisconnectingReportClientPresence2(player)
playerJoinTimes[player.userId] = nil
-- Games API
local success, err = pcall(function() return SendMessageToGamesApi("PlayerRemoving", player) end)
if (not success) then
PrintErrorMessage("playersService.PlayerRemoving error updating games api = " .. err)
end
-- Game Instances API
success, err = pcall(function() return SendMessageToGameInstancesApi("PlayerRemoving") end)
if (not success) then
PrintErrorMessage("playersService.PlayerRemoving error updating game instances api = = " .. err)
end
-- Api Proxy - Presence
success, err = pcall(function() return UpdatePresence(player, true) end)
if (not success) then
PrintErrorMessage("playersService.PlayerRemoving error updating presence = " .. err)
end
end
if (kickDuplicatePlayersRCC or isCloudEdit) then
playersService.PlayerConnecting:connect(function(player)
pcall(function() onPlayerConnectingReportClientPresence2(player) end)
pcall(function() onPlayerConnectingReportClientPresence(player) end)
end)
playersService.PlayerDisconnecting:connect(function(player)
pcall(function() onPlayerDisconnectingReportClientPresence2(player) end)
pcall(function() onPlayerDisconnectingReportClientPresence(player) end)
end)
else
playersService.PlayerAdded:connect(onPlayerConnectingReportClientPresence)
playersService.PlayerAdded:connect(onPlayerConnectingReportClientPresence2)
playersService.PlayerRemoving:connect(onPlayerDisconnectingReportClientPresence)
playersService.PlayerRemoving:connect(onPlayerDisconnectingReportClientPresence2)
end
game.Close:connect(function()
-- when game closes, notify Game Instances API
local fullUrl = gameInstancesApiUrl .. "/v1/Close/?apiKey=" .. apiKey .. "&gameId=" .. gameId .."&placeId=" .. placeId .."&universeId=" .. universeId
PrintDebugMessage("Calling Game Instances API. Source: GameClose, URL: " .. fullUrl)
game:HttpPost(fullUrl, "", true)
end)
@@ -0,0 +1,335 @@
-- AnimationManifest.txt
-- V 1.0.2
-- Exports multiple frames
-- https://spocktopus.roblox.local/Internals_of_3D_Animated_Thumbnails
baseUrl, characterAppearanceUrl, animationUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ScriptContext").ScriptsDisabled = true
local player = game:GetService("Players"):CreateLocalPlayer(0)
player.CharacterAppearance = characterAppearanceUrl
player:LoadCharacterBlocking()
local FRAME_RATE = 60
local MAX_TIME = 2
local originalJointCFramesMap = {}
local originalPartsCFramesMap = {}
local function getOriginalJointCFrames(object)
for _, child in pairs(object:GetChildren()) do
if child:IsA("Motor6D") then
originalJointCFramesMap[child] = child.C1
end
if child:IsA("BasePart") then
originalPartsCFramesMap[child] = child.CFrame
end
getOriginalJointCFrames(child)
end
end
local character = player.Character
local humanoid = character:FindFirstChildOfClass("Humanoid")
getOriginalJointCFrames(character)
local animator = humanoid:FindFirstChild("Animator")
local animationObj = Instance.new("Animation")
local animationObjects = game:GetObjects(animationUrl)
local animations = {}
local rotateCharacter = false
local function getAnimations(model)
for _, child in pairs(model:GetChildren()) do
if child:IsA("StringValue") and child.Name == "swim" then
rotateCharacter = true
elseif child:IsA("StringValue") and child.Name == "swimidle" then
-- Don't include swimidle if we are thumnailing the swim animation
if child.Parent and child.Parent:FindFirstChild("swim") then
return
end
end
if child:IsA("Animation") then
table.insert(animations, child)
else
getAnimations(child)
end
end
end
for _, animationModel in pairs(animationObjects) do
getAnimations(animationModel)
end
if rotateCharacter then
local rootPart = character:FindFirstChild("HumanoidRootPart")
if rootPart then
rootPart.CFrame = rootPart.CFrame * CFrame.Angles(math.rad(-90), 0, 0)
end
end
local animationId = ""
local bestWeight = -1
for _, anim in pairs(animations) do
local weight = anim:FindFirstChild("Weight")
if weight then
if weight.Value > bestWeight then
bestWeight = weight.Value
animationId = anim.AnimationId
end
else
animationId = anim.AnimationId
end
end
local KeyframeSequenceProvider = game:GetService("KeyframeSequenceProvider")
local keyframeSequence = KeyframeSequenceProvider:GetKeyframeSequence(animationId)
animationObj.AnimationId = KeyframeSequenceProvider:RegisterActiveKeyframeSequence(keyframeSequence)
for _, track in pairs(humanoid:GetPlayingAnimationTracks()) do
track:Stop(0)
end
local track = humanoid:LoadAnimation(animationObj)
track:Play(0)
local finished = false
local function stepNextFrame()
local delta = 1/FRAME_RATE
if track.TimePosition + delta > track.Length then
delta = track.Length - track.TimePosition
end
if delta <= 0.005 or track.TimePosition >= track.Length or finished then
return false
end
local preStepTimePosition = track.TimePosition
animator:StepAnimations(delta)
if track.TimePosition <= preStepTimePosition then
finished = true
end
return true
end
local math_floor = math.floor
local math_sin = math.sin
local math_cos = math.cos
local math_min = math.min
local math_max = math.max
local function round(number)
return math_floor(number*10000 + 0.5)/10000
end
local function vector3ToTable(vector)
return {
x = round(vector.X),
y = round(vector.Y),
z = round(vector.Z)
}
end
local animationData = {}
while stepNextFrame() do
local frameAnimationData = {}
for _, obj in pairs(character:GetChildren()) do
if obj:IsA("BasePart") or obj:IsA("Accoutrement") then
local part = obj
if obj:IsA("Accoutrement") then
part = obj:FindFirstChild("Handle")
end
if part and part.Name ~= "HumanoidRootPart" then
local posAndRotation = {}
posAndRotation["Position"] = vector3ToTable(part.Position)
local axis, angle = part.CFrame:toAxisAngle()
local halfAngle = angle/2
posAndRotation["Rotation"] = {
x = round(math_sin(halfAngle)*axis.X),
y = round(math_sin(halfAngle)*axis.Y),
z = round(math_sin(halfAngle)*axis.Z),
w = round(math_cos(halfAngle))
}
frameAnimationData[obj.Name] = posAndRotation
end
end
end
table.insert(animationData, frameAnimationData)
end
-- Take note of finishing CFrames for the animation
-- The CameraResult position will be transformed based on this
local animatedPartPositionsMap = {}
for part, _ in pairs(originalPartsCFramesMap) do
animatedPartPositionsMap[part] = part.Position
end
-- Restore original CFrames
for motor, origC1 in pairs(originalJointCFramesMap) do
motor.C1 = origC1
end
for part, cframe in pairs(originalPartsCFramesMap) do
part.Anchored = true
part.CFrame = cframe
end
local partsArray = {}
for _, obj in pairs(character:GetChildren()) do
if obj:IsA("BasePart") or obj:IsA("Accoutrement") then
if obj.Name ~= "HumanoidRootPart" then
table.insert(partsArray, obj)
end
end
end
local string_sub = string.sub
local string_len = string.len
function strEndsWith(str, val)
return string_sub(str, -string_len(val)) == val
end
function replaceChar(pos, str, r)
return str:sub(1, pos - 1) ..r.. str:sub(pos + 1)
end
game:GetService("Selection"):Set(partsArray)
local objsStrOutput = nil
objsStrOutput, contentIdsUsed = game:GetService("ThumbnailGenerator"):Click("SplitObjs", 0, 0, true)
local decodedObjsStrOutput = game:GetService("HttpService"):JSONDecode(objsStrOutput)
local partObjsResult = {}
local textures = {}
local cameraResult = nil
local totalAABB = {
["min"] = {},
["max"] = {}
}
local function addToTotalAABB(partName, partAABB)
local part = character:FindFirstChild(partName)
if part and animatedPartPositionsMap[part] then
local currentPosition = part.Position
local animatedPosition = animatedPartPositionsMap[part]
local offset = animatedPosition - currentPosition
local minJSON = partAABB["min"]
local currentMin = Vector3.new(minJSON.x, minJSON.y, minJSON.z)
currentMin = Vector3.new(currentMin.X, currentMin.Y + offset.Y, currentMin.Z)
partAABB["min"] = vector3ToTable(currentMin)
local maxJSON = partAABB["min"]
local currentMax = Vector3.new(maxJSON.x, maxJSON.y, maxJSON.z)
currentMax = Vector3.new(currentMax.X, currentMax.Y + offset.Y, currentMax.Z)
partAABB["min"] = vector3ToTable(currentMax)
end
for xyzkey, val in pairs(partAABB["min"]) do
if not totalAABB["min"][xyzkey] then
totalAABB["min"][xyzkey] = val
else
totalAABB["min"][xyzkey] = math_min(totalAABB["min"][xyzkey], val)
end
end
for xyzkey, val in pairs(partAABB["max"]) do
if not totalAABB["max"][xyzkey] then
totalAABB["max"][xyzkey] = val
else
totalAABB["max"][xyzkey] = math_max(totalAABB["max"][xyzkey], val)
end
end
end
local function resolveCameraResult(partName, cameraJSON)
local part = character:FindFirstChild(partName)
if part and animatedPartPositionsMap[part] then
local currentPosition = part.Position
local animatedPosition = animatedPartPositionsMap[part]
local offset = animatedPosition - currentPosition
local positionJSON = cameraJSON["position"]
local cameraPosition = Vector3.new(positionJSON.x, positionJSON.y, positionJSON.z)
cameraPosition = cameraPosition + offset
cameraJSON["position"] = vector3ToTable(cameraPosition)
end
if partName == "Head" then
cameraResult = cameraJSON
elseif cameraResult == nil then -- Fallback if Head doesn't exist for some reason
cameraResult = cameraJSON
end
end
-- Process the SplitObjs output to consolidate it for the animation output.
-- The data for common textures is mapped to the same output in the textures table.
-- The Camera and AABB fields are consolidated into global fields.
for key, val in pairs(decodedObjsStrOutput) do
local decodedPartObj = game:GetService("HttpService"):JSONDecode(val)
if decodedPartObj["files"] then
local files = decodedPartObj["files"]
for fileName, fileInfo in pairs(files) do
local fileContent = fileInfo.content
if strEndsWith(fileName, ".png") then
local newFileName = fileName
while textures[newFileName] and textures[newFileName].content ~= fileContent do
local charsFromEnd = string_len("Tex.png")
local replacePos = string_len(newFileName) - charsFromEnd
local newNumber = tostring(tonumber(newFileName:sub(replacePos, replacePos)) + 1)
newFileName = replaceChar(replacePos, newFileName, newNumber)
end
textures[newFileName] = {
content = fileContent
}
files["texture"] = newFileName
files[fileName] = nil
end
end
end
-- Calculate total aabb
if decodedPartObj["AABB"] then
addToTotalAABB(key, decodedPartObj["AABB"])
decodedPartObj["AABB"] = nil
end
-- Resolve overall Camera JSON.
if decodedPartObj["camera"] then
resolveCameraResult(key, decodedPartObj["camera"])
decodedPartObj["camera"] = nil
end
partObjsResult[key] = decodedPartObj
end
-- Special camera position and direction for rotated character
if rotateCharacter then
local rootPart = character:FindFirstChild("HumanoidRootPart")
if rootPart then
local cameraOffset = Vector3.new(6, 5, 7) * .7
local cameraPosition = Vector3.new(rootPart.Position.X, rootPart.Position.Y, rootPart.Position.Z) - cameraOffset
local cameraDirection = (rootPart.Position - cameraPosition).unit
cameraResult["position"] = vector3ToTable(cameraPosition)
cameraResult["direction"] = vector3ToTable(cameraDirection)
end
end
local resultData = {
Frames = animationData,
Camera = cameraResult,
AABB = totalAABB,
PartObjs = partObjsResult,
Textures = textures
}
return game:GetService("HttpService"):JSONEncode(resultData), contentIdsUsed
@@ -0,0 +1,24 @@
-- Avatar v1.0.1
-- This is the thumbnail script for R6 avatars. Straight up and down, with the right arm out if they have a gear.
characterAppearanceUrl, baseUrl, fileExtension, x, y = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ScriptContext").ScriptsDisabled = true
local player = game:GetService("Players"):CreateLocalPlayer(0)
player.CharacterAppearance = characterAppearanceUrl
print(characterAppearanceUrl)
player:LoadCharacterBlocking()
-- 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)
@@ -0,0 +1,120 @@
-- AvatarAnimation V 1.0.3
-- Creates a thumbnail from the middle Keyframe of an animation.
-- Example arguments:
-- https://www.sitetest3.robloxlabs.com/Asset/AvatarAccoutrements.ashx?AvatarHash=80dcfa39a8e90f690d2be0e56239abbe&AssetIDs=42900214,32357663,32357631,32357619,32357584,32357558&ResolvedAvatarType=R15,
-- http://www.sitetest3.robloxlabs.com/,
-- Png,
-- 600,
-- 600,
-- http://www.sitetest3.robloxlabs.com/Asset/?hash=ca005a9e83ac95bd5068029afdc65496
characterAppearanceUrl, baseUrl, fileExtension, x, y, animationUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ScriptContext").ScriptsDisabled = true
local player = game:GetService("Players"):CreateLocalPlayer(0)
player.CharacterAppearance = characterAppearanceUrl
player:LoadCharacterBlocking()
local function getJointBetween(part0, part1)
local foundJoint = nil
for _, obj in pairs(part1:GetChildren()) do
if obj:IsA("Motor6D") and obj.Part0 == part0 then
return obj
end
end
end
local function applyR15Pose(character, poseKeyframe)
local function recurApplyPoses(parentPose, poseObject)
if parentPose then
local joint = getJointBetween(character[parentPose.Name], character[poseObject.Name])
joint.C1 = joint.C1 * poseObject.CFrame:inverse()
end
for _, subPose in pairs(poseObject:GetSubPoses()) do
recurApplyPoses(poseObject, subPose)
end
end
for _, poseObj in pairs(poseKeyframe:GetPoses()) do
recurApplyPoses(nil, poseObj)
end
end
local animationObjects = game:GetObjects(animationUrl)
local poseAnimation = nil
local animations = {}
local rotateCharacter = false
local thumbnailCamera = nil
local function getAnimations(model)
for _, child in pairs(model:GetChildren()) do
if child:IsA("Animation") then
if string.lower(model.Name) == "pose" then
poseAnimation = child
else
table.insert(animations, child)
end
else
getAnimations(child)
end
if child:IsA("Camera") and child.Name == "ThumbnailCamera" then
thumbnailCamera = child:Clone()
end
if child:IsA("StringValue") and child.Name == "swim" then
rotateCharacter = true
end
end
end
for _, animationModel in pairs(animationObjects) do
getAnimations(animationModel)
end
local KeyframeSequenceProvider = game:GetService("KeyframeSequenceProvider")
local keyframes = {}
if poseAnimation then
local kfs = KeyframeSequenceProvider:GetKeyframeSequence(poseAnimation.AnimationId)
local animKeyframes = kfs:GetKeyframes()
for _, keyframe in pairs(animKeyframes) do
table.insert(keyframes, keyframe)
end
else
for _, animation in pairs(animations) do
local kfs = KeyframeSequenceProvider:GetKeyframeSequence(animation.AnimationId)
local animKeyframes = kfs:GetKeyframes()
for _, keyframe in pairs(animKeyframes) do
table.insert(keyframes, keyframe)
end
end
end
local keyframe = keyframes[math.max(1, math.floor(#keyframes/2))]
applyR15Pose(player.Character, keyframe)
if rotateCharacter then
local rootPart = player.Character:FindFirstChild("HumanoidRootPart")
if rootPart then
rootPart.CFrame = rootPart.CFrame * CFrame.Angles(math.rad(-90), 0, 0)
if not thumbnailCamera then
local camera = Instance.new("Camera")
camera.Name = "ThumbnailCamera"
local rotatedCameraOffset = Vector3.new(6, 5, 7) * .7
camera.CFrame = CFrame.new(Vector3.new(rootPart.Position.X, rootPart.Position.Y, rootPart.Position.Z) - rotatedCameraOffset, rootPart.Position)
camera.Parent = player.Character
end
end
end
if thumbnailCamera then
thumbnailCamera.Parent = player.Character
end
return game:GetService("ThumbnailGenerator"):Click(fileExtension, x, y, --[[hideSky = ]] true)
@@ -0,0 +1,164 @@
-- Avatar_R15_Action v1.1.0
-- For R6, this generates the normal with/without gear pose. For R15 it positions their body in an action pose.
baseUrl, characterAppearanceUrl, fileExtension, x, y = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ScriptContext").ScriptsDisabled = true
local player = game:GetService("Players"):CreateLocalPlayer(0)
player.CharacterAppearance = characterAppearanceUrl
player:LoadCharacterBlocking()
local poseAnimationId = "http://www.roblox.com/asset/?id=532421348"
local function getJointBetween(part0, part1)
for _, obj in pairs(part1:GetChildren()) do
if obj:IsA("Motor6D") and obj.Part0 == part0 then
return obj
end
end
end
local function applyKeyframe(character, poseKeyframe)
local function recurApplyPoses(parentPose, poseObject)
if parentPose then
local joint = getJointBetween(character[parentPose.Name], character[poseObject.Name])
if joint and poseObject.Weight ~= 0 then
joint.C1 = poseObject.CFrame:inverse() + joint.C1.p
end
end
for _, subPose in pairs(poseObject:GetSubPoses()) do
recurApplyPoses(poseObject, subPose)
end
end
for _, poseObj in pairs(poseKeyframe:GetPoses()) do
recurApplyPoses(nil, poseObj)
end
end
local function applyR15Pose(character)
local poseKeyframSequence = game:GetService("KeyframeSequenceProvider"):GetKeyframeSequence(poseAnimationId)
local poseKeyframe = poseKeyframSequence:GetKeyframes()[1]
applyKeyframe(character, poseKeyframe)
end
local function findAttachmentsRecur(parent, resultTable, returnDictionary)
for _, obj in pairs(parent:GetChildren()) do
if obj:IsA("Attachment") then
if returnDictionary then
resultTable[obj.Name] = obj
else
resultTable[#resultTable + 1] = obj
end
elseif not obj:IsA("Tool") and not obj:IsA("Accoutrement") then -- Leave out tools and accoutrements in the character
findAttachmentsRecur(obj, resultTable, returnDictionary)
end
end
end
local function findAttachmentsInTool(tool)
local attachments = {}
findAttachmentsRecur(tool, attachments, false)
return attachments
end
local function findAttachmentsInCharacter(character)
local attachments = {}
findAttachmentsRecur(character, attachments, true)
return attachments
end
local function weldAttachments(attach1, attach2)
local weld = Instance.new("Weld")
weld.Part0 = attach1.Parent
weld.Part1 = attach2.Parent
weld.C0 = attach1.CFrame
weld.C1 = attach2.CFrame
weld.Parent = attach1.Parent
return weld
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
local function doR15ToolPose(character, humanoid, tool)
local characterAttachments = findAttachmentsInCharacter(character)
local toolAttachments = findAttachmentsInTool(tool)
local foundAttachments = false
-- If matching attachments exist in the gear then weld them and do the "action" R15 pose.
-- Otherwise keep the R15 in the T-Pose position and just raise the arm.
for _, attachment in pairs(toolAttachments) do
local matchingAttachment = characterAttachments[attachment.Name]
if matchingAttachment then
foundAttachments = true
weldAttachments(matchingAttachment, attachment)
end
end
if foundAttachments then
tool.Parent = character
applyR15Pose(character)
local toolPose = tool:FindFirstChild("ThumbnailPose")
if toolPose and toolPose:IsA("Keyframe") then
applyKeyframe(character, toolPose)
end
else
tool.Parent = nil
local rightShoulderJoint = getJointBetween(character.UpperTorso, character.RightUpperArm)
if rightShoulderJoint then
rightShoulderJoint.C1 = rightShoulderJoint.C1 * CFrame.new(0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 1, 0):inverse()
end
if tool:FindFirstChild("Handle") then
local attachment = findFirstMatchingAttachment(character, "RightGripAttachment")
if attachment then
tool.Handle.CFrame = attachment.Parent.CFrame * attachment.CFrame * tool.Grip:inverse()
end
end
humanoid:EquipTool(tool)
end
end
local character = player.Character
if character then
local tool = character:FindFirstChildOfClass("Tool")
local humanoid = character:FindFirstChildOfClass("Humanoid")
local animateScript = character:FindFirstChild("Animate")
if animateScript then
local equippedPoseValue = animateScript:FindFirstChild("Pose") or animateScript:FindFirstChild("pose")
if equippedPoseValue then
local poseAnim = equippedPoseValue:FindFirstChildOfClass("Animation")
if poseAnim then
poseAnimationId = poseAnim.AnimationId
end
end
end
if humanoid then
if humanoid.RigType == Enum.HumanoidRigType.R6 then
if tool then
character.Torso["Right Shoulder"].CurrentAngle = math.rad(90)
end
elseif humanoid.RigType == Enum.HumanoidRigType.R15 then
if tool then
doR15ToolPose(character, humanoid, tool)
else
applyR15Pose(character)
end
end
end
end
return game:GetService("ThumbnailGenerator"):Click(fileExtension, x, y, --[[hideSky = ]] true)
@@ -0,0 +1,43 @@
--Avatar_R15_Standard v1.0.1
--Pose R6 characters in the normal way. For R15, have them in the same pose, and raise their arm up if they have gear.
baseUrl, characterAppearanceUrl, fileExtension, x, y = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ScriptContext").ScriptsDisabled = true
local player = game:GetService("Players"):CreateLocalPlayer(0)
player.CharacterAppearance = characterAppearanceUrl
player:LoadCharacterBlocking()
local function getJointBetween(part0, part1)
for _, obj in pairs(part1:GetChildren()) do
if obj:IsA("Motor6D") and obj.Part0 == part0 then
return obj
end
end
end
local function doR15ToolPose(rig)
local rightShoulderJoint = getJointBetween(rig.UpperTorso, rig.RightUpperArm)
if rightShoulderJoint then
rightShoulderJoint.C1 = rightShoulderJoint.C1 * CFrame.new(0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 1, 0):inverse()
end
end
-- Raise right arm up to hold gear.
local character = player.Character
if character then
if character:FindFirstChildOfClass("Tool") then
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
if humanoid.RigType == Enum.HumanoidRigType.R6 then
character.Torso['Right Shoulder'].CurrentAngle = math.rad(90)
elseif humanoid.RigType == Enum.HumanoidRigType.R15 then
doR15ToolPose(character)
end
end
end
end
return game:GetService('ThumbnailGenerator'):Click(fileExtension, x, y, --[[hideSky = ]] true)
@@ -0,0 +1,157 @@
-- BodyPart v1.0.5
-- See http://wiki.roblox.com/index.php?title=R15_Compatibility_Guide#Package_Parts for details on how body parts work with R15
local assetUrl, baseUrl, fileExtension, x, y, R6RigUrl = ...
pcall(function() game:GetService('ContentProvider'):SetBaseUrl(baseUrl) end)
game:GetService('ScriptContext').ScriptsDisabled = true
local objects = game:GetObjects(assetUrl)
local useR15 = false
local useR15NewNames = false
local bodyPartProportion = "Classic"
for _, object in pairs(objects) do
if object:IsA("Folder") and (object.Name == "R15" or object.Name == "R15ArtistIntent") then
useR15 = true
-- Check to see if there are native scale parts in this object
local partScaleType = object:FindFirstChild("AvatarPartScaleType", true)
if partScaleType then
bodyPartProportion = partScaleType.Value
end
break
end
end
local R15RigUrl = "http://www.roblox.com/asset/?id=516159357"
for _, object in pairs(objects) do
if object:IsA("Folder") and object.Name == "R15ArtistIntent" then
useR15NewNames = true
if bodyPartProportion == "Classic" then
R15RigUrl = "http://www.roblox.com/asset/?id=1664543044"
else
R15RigUrl = "http://www.roblox.com/asset/?id=2337256345"
end
break
end
end
local mannequin = nil
if useR15 then
mannequin = game:GetObjects(R15RigUrl)[1]
else
mannequin = game:GetObjects(R6RigUrl)[1]
end
mannequin.Humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None
mannequin.Parent = workspace
--game:GetObjects(customUrl)[1].Parent = mannequin
function addFolderChildren(folder)
for _, child in pairs(folder:GetChildren()) do
local existingBodyPart = mannequin:FindFirstChild(child.Name)
if existingBodyPart then
existingBodyPart:Destroy()
end
child.Parent = mannequin
end
end
local r15FolderName = "R15"
if (useR15 and useR15NewNames) then
r15FolderName = "R15ArtistIntent"
end
for _, object in pairs(objects) do
if useR15 and object:IsA("Folder") and object.Name == r15FolderName then
addFolderChildren(object)
elseif not useR15 and object:IsA("Folder") and object.Name == "R6" then
addFolderChildren(object)
elseif not (object:IsA("Folder") and string.find(object.Name, "R15")) then -- There will now be MULTIPLE R15 Folders. Ignore the ones we didn't search for.
object.Parent = mannequin
end
end
local function buildJoint(parentAttachment, partForJointAttachment)
local jointName = parentAttachment.Name:gsub("RigAttachment", "")
local motor = partForJointAttachment.Parent:FindFirstChild(jointName)
if not motor then
motor = Instance.new("Motor6D")
end
motor.Name = jointName
motor.Part0 = parentAttachment.Parent
motor.Part1 = partForJointAttachment.Parent
motor.C0 = parentAttachment.CFrame
motor.C1 = partForJointAttachment.CFrame
motor.Parent = partForJointAttachment.Parent
end
-- Builds an R15 rig from the attachments in the parts
function buildRigFromAttachments(currentPart, lastPart)
local validSiblings = {}
for _, sibling in pairs(currentPart.Parent:GetChildren()) do
-- Don't find matching attachment in the current part being processed.
-- Don't visit the last part visited again, this would cause an infinite loop.
if sibling:IsA("BasePart") and sibling ~= currentPart and sibling ~= lastPart then
table.insert(validSiblings, sibling)
end
end
local function processRigAttachment(attachment)
for _, sibling in pairs(validSiblings) do
local matchingAttachment = sibling:FindFirstChild(attachment.Name)
if matchingAttachment then
buildJoint(attachment, matchingAttachment)
buildRigFromAttachments(matchingAttachment.Parent, currentPart)
end
end
end
for _, object in pairs(currentPart:GetChildren()) do
if object:IsA("Attachment") and string.find(object.Name, "RigAttachment") then
processRigAttachment(object)
end
end
end
if useR15 then
-- Build R15 rig
local humanoidRootPart = mannequin:WaitForChild("HumanoidRootPart")
humanoidRootPart.CFrame = CFrame.new(Vector3.new(0, 5, 0)) * CFrame.Angles(0, math.pi, 0)
humanoidRootPart.Anchored = true
buildRigFromAttachments(humanoidRootPart)
local humanoid = mannequin:WaitForChild("Humanoid")
if humanoid then
local typeObject = humanoid:FindFirstChild("BodyTypeScale")
if typeObject == nil then
typeObject = Instance.new("NumberValue")
typeObject.Name = "BodyTypeScale"
typeObject.Value = 0
typeObject.Parent = humanoid
end
local proportionObject = humanoid:FindFirstChild("BodyProportionScale")
if proportionObject == nil then
proportionObject = Instance.new("NumberValue")
proportionObject.Name = "BodyProportionScale"
proportionObject.Value = 0
proportionObject.Parent = humanoid
end
if bodyPartProportion == "ProportionsNormal" then
typeObject.Value = 1
proportionObject.Value = 0
elseif bodyPartProportion == "ProportionsSlender" then
typeObject.Value = 1
proportionObject.Value = 1
end
end
end
return game:GetService("ThumbnailGenerator"):Click(fileExtension, x, y, --[[hideSky = ]] true)
@@ -0,0 +1,47 @@
-- Closeup v1.0.3
-- Used for avatar closeup
baseUrl, characterAppearanceUrl, fileExtension, x, y, quadratic, baseHatZoom, maxHatZoom, cameraOffsetX, cameraOffsetY = ...
pcall(function() game:GetService('ContentProvider'):SetBaseUrl(baseUrl) end)
game:GetService('ScriptContext').ScriptsDisabled = true
local player = game:GetService("Players"):CreateLocalPlayer(0)
player.CharacterAppearance = characterAppearanceUrl
player:LoadCharacterBlocking()
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
workspace.CurrentCamera = camera -- I added this
end
return game:GetService("ThumbnailGenerator"):Click(fileExtension, x, y, --[[hideSky = ]] true)
@@ -0,0 +1,23 @@
-- Decal v1.0.2
-- Used for faces and decals
assetUrl, fileExtension, x, y, baseUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService('ScriptContext').ScriptsDisabled = true
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}
@@ -0,0 +1,17 @@
-- Gear v1.0.2
assetUrl, fileExtension, x, y, baseUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ScriptContext").ScriptsDisabled = true
for _, object in pairs(game:GetObjects(assetUrl)) do
object.Parent = workspace
end
local ThumbnailGenerator = game:GetService("ThumbnailGenerator")
if string.lower(fileExtension) == "obj" then
return ThumbnailGenerator:Click(fileExtension, x, y, --[[hideSky = ]] true, --[[crop =]] true)
end
return ThumbnailGenerator:Click(fileExtension, x, y, --[[hideSky = ]] true, --[[crop =]] true)
@@ -0,0 +1,15 @@
-- Hat v1.0.1
assetUrl, fileExtension, x, y, baseUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ScriptContext").ScriptsDisabled = true
game:GetObjects(assetUrl)[1].Parent = workspace
local ThumbnailGenerator = game:GetService("ThumbnailGenerator")
if string.lower(fileExtension) == "obj" then
return ThumbnailGenerator:Click(fileExtension, x, y, --[[hideSky = ]] true, --[[crop = ]] true)
end
return ThumbnailGenerator:Click(fileExtension, x, y, --[[hideSky = ]] true, --[[crop = ]] true)
@@ -0,0 +1,27 @@
-- Head v1.0.1
assetUrl, fileExtension, x, y, baseUrl, mannequinId = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ScriptContext").ScriptsDisabled = true
local mannequin = game:GetObjects(baseUrl.. "/asset/?id=" .. tostring(mannequinId))[1]
mannequin.Humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None
mannequin.Parent = workspace
mannequin.Head.BrickColor = BrickColor.Gray()
if mannequin.Head:FindFirstChild("Mesh") then
mannequin.Head.Mesh:Destroy()
end
for _, child in pairs(mannequin:GetChildren()) do
if child:IsA("BasePart") and child.Name ~= "Head" then
child:Destroy()
end
end
local mesh = game:GetObjects(assetUrl)[1]
mesh.Parent = mannequin.Head
return game:GetService("ThumbnailGenerator"):Click(fileExtension, x, y, --[[hideSky = ]] true)
@@ -0,0 +1,36 @@
imageId, baseUrl, fileExtension, x, y = ...
local ThumbnailGenerator = game:GetService("ThumbnailGenerator")
local ContentProvider = game:GetService("ContentProvider")
game:GetService('StarterGui'):SetCoreGuiEnabled(Enum.CoreGuiType.All, false);
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ScriptContext").ScriptsDisabled = true
local IsSuccess, actualImageId = pcall(function()
local Asset = game:GetObjects(baseUrl.."/asset/?id="..tostring(imageId))[1]
if Asset:IsA("Decal") then
return Asset.Texture
end
for _, child in pairs(Asset:GetDescendants()) do
if child:IsA("Decal") then
return child.Texture
end
end
end)
local AssetURL
if not IsSuccess then
actualImageId = imageId
AssetURL = baseUrl.."/asset/?id="..tostring(imageId)
else
AssetURL = actualImageId
end
local success, result = pcall(function()
return ThumbnailGenerator:ClickTexture(AssetURL, fileExtension, x, y)
end)
if not success then
result = "iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAQAAABecRxxAAAEnklEQVR42u3UMQEAAAjDMOZf9JCAABIJPZp2gKdiAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYABiAAYABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAGAAIoABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYABiAAYABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAGAABgAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAYABiAAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAYABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAGAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAYABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABwW1Dy/i5f+lAuAAAAAElFTkSuQmCC" -- 1x1 transparent pixel
end
return result
@@ -0,0 +1,16 @@
-- Mesh v1.0.1
assetUrl, fileExtension, x, y, baseUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ScriptContext").ScriptsDisabled = true
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)
@@ -0,0 +1,32 @@
-- MeshPart v1.0.1
assetUrl, fileExtension, x, y, baseUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ScriptContext").ScriptsDisabled = true
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.. "/static/img/LuaThumbnail.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)
@@ -0,0 +1,32 @@
-- Model v1.0.1
assetUrl, fileExtension, x, y, baseUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ScriptContext").ScriptsDisabled = true
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.. "/static/img/LuaThumbnail.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)
@@ -0,0 +1,349 @@
-- Package v1.1.6
-- See http://wiki.roblox.com/index.php?title=R15_Compatibility_Guide#Package_Parts for details on how body parts work with R15
local assetUrls, baseUrl, fileExtension, x, y, R6RigUrl, customTextureUrls = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ScriptContext").ScriptsDisabled = true
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 R15ArtistIntentAssets = {}
local R15Assets = {}
local R6Assets = {}
local bothAssets = {}
local useR15ArtistIntent = false
local useR15 = true
local poseAnimationId = nil
local poseValueFound = false
local function processR15Anim(animFolder)
local function processStrValue(strValue)
local animation = strValue:FindFirstChildOfClass("Animation")
if not animation then
return
end
-- By default the pose animation will be used for the thumbnail
-- If the pose animation doesn't exist then the idle will be used, otherwise the first animation will be used.
if string.lower(strValue.Name) == "pose" then
poseValueFound = true
poseAnimationId = animation.AnimationId
elseif not poseValueFound and string.lower(strValue.Name) == "idle" then
poseAnimationId = animation.AnimationId
elseif not poseAnimationId then
poseAnimationId = animation.AnimationId
end
end
for _, obj in pairs(animFolder:GetChildren()) do
if obj:IsA("StringValue") then
processStrValue(obj)
end
end
end
local assetUrlsList = split(assetUrls, ";")
for _, assetUrl in pairs(assetUrlsList) do
local currObjects = game:GetObjects(assetUrl)
for _,object in pairs(currObjects) do
if object:IsA("Folder") and object.Name == "R15ArtistIntent" then
for _, child in pairs(object:GetChildren()) do
table.insert(R15ArtistIntentAssets, child)
end
elseif object:IsA("Folder") and object.Name == "R15Fixed" then
-- Do nothing. We just don't want this to be dumped in bothAssets
elseif object:IsA("Folder") and object.Name == "R15" then
for _, child in pairs(object:GetChildren()) do
table.insert(R15Assets, child)
end
elseif object:IsA("Folder") and object.Name == "R6" then
foundR6CompatibleParts = true
for _, child in pairs(object:GetChildren()) do
table.insert(R6Assets, child)
end
elseif object:IsA("CharacterMesh") then
-- Legacy body part format using a CharacterMesh
table.insert(R6Assets, object)
elseif object:IsA("Folder") and object.Name == "R15Anim" then
processR15Anim(object)
else
table.insert(bothAssets, object)
end
end
end
-- if the package doesn't contain animations, use this pose.
poseAnimationId = poseAnimationId or "http://www.roblox.com/asset/?id=532421348"
-- Only use R6 if we found body parts that are only compatible with R15
if #R6Assets ~= 0 and #R15Assets == 0 and #R15ArtistIntentAssets == 0 then
useR15 = false
end
local R15RigUrl = "http://www.roblox.com/asset/?id=516159357"
if useR15 and #R15ArtistIntentAssets > 0 then
useR15ArtistIntent = true
R15RigUrl = "http://www.roblox.com/asset/?id=1664543044"
end
local mannequin = nil
if useR15 then
mannequin = game:GetObjects(R15RigUrl)[1]
if (useR15ArtistIntent) then
for _, obj in pairs(R15ArtistIntentAssets) do
table.insert(bothAssets, obj)
end
else
for _,obj in pairs(R15Assets) do
table.insert(bothAssets, obj)
end
end
else
mannequin = game:GetObjects(R6RigUrl)[1]
for _,obj in pairs(R6Assets) do
table.insert(bothAssets, obj)
end
end
mannequin.Humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None
mannequin.Parent = workspace
local tool = nil
local accoutrements = {}
for _, currObject in pairs(bothAssets) do
if currObject:IsA("BasePart") then
local existingBodyPart = mannequin:FindFirstChild(currObject.Name)
if existingBodyPart ~= nil then
existingBodyPart:Destroy()
end
end
if currObject:IsA("Tool") then
if useR15 then
tool = currObject
else
mannequin.Torso["Right Shoulder"].CurrentAngle = math.rad(90)
currObject.Parent = mannequin
end
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
local function buildJoint(parentAttachment, partForJointAttachment)
local jointName = parentAttachment.Name:gsub("RigAttachment", "")
local motor = partForJointAttachment.Parent:FindFirstChild(jointName)
if not motor then
motor = Instance.new("Motor6D")
end
motor.Name = jointName
motor.Part0 = parentAttachment.Parent
motor.Part1 = partForJointAttachment.Parent
motor.C0 = parentAttachment.CFrame
motor.C1 = partForJointAttachment.CFrame
motor.Parent = partForJointAttachment.Parent
end
-- Builds an R15 rig from the attachments in the parts
function buildRigFromAttachments(currentPart, lastPart)
local validSiblings = {}
for _, sibling in pairs(currentPart.Parent:GetChildren()) do
-- Don't find matching attachment in the current part being processed.
-- Don't visit the last part visited again, this would cause an infinite loop.
if sibling:IsA("BasePart") and sibling ~= currentPart and sibling ~= lastPart then
table.insert(validSiblings, sibling)
end
end
local function processRigAttachment(attachment)
for _, sibling in pairs(validSiblings) do
local matchingAttachment = sibling:FindFirstChild(attachment.Name)
if matchingAttachment then
buildJoint(attachment, matchingAttachment)
buildRigFromAttachments(matchingAttachment.Parent, currentPart)
end
end
end
for _, object in pairs(currentPart:GetChildren()) do
if object:IsA("Attachment") and string.find(object.Name, "RigAttachment") then
processRigAttachment(object)
end
end
end
local function getJointBetween(part0, part1)
for _, obj in pairs(part1:GetChildren()) do
if obj:IsA("Motor6D") and obj.Part0 == part0 then
return obj
end
end
end
local function applyR15ToolPose(rig)
local upperTorso = rig:FindFirstChild("UpperTorso")
local rightUpperArm = rig:FindFirstChild("RightUpperArm")
if upperTorso and rightUpperArm then
local rightShoulderJoint = getJointBetween(upperTorso, rightUpperArm)
if rightShoulderJoint then
rightShoulderJoint.C1 = rightShoulderJoint.C1 * CFrame.new(0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 1, 0):inverse()
end
end
end
-- Applies the middle keyframe of a pose to a given character.
local function applyPoseToCharacter(character, poseAnimationId)
local poseKeyframSequence = game:GetService("KeyframeSequenceProvider"):GetKeyframeSequence(poseAnimationId)
local keyframes = poseKeyframSequence:GetKeyframes()
local poseKeyframe = keyframes[math.max(1, math.floor(#keyframes/2))]
local function recurApplyPoses(parentPose, poseObject)
if parentPose then
local joint = getJointBetween(character[parentPose.Name], character[poseObject.Name])
if joint then
joint.C1 = joint.C1 * poseObject.CFrame:inverse()
end
end
for _, subPose in pairs(poseObject:GetSubPoses()) do
recurApplyPoses(poseObject, subPose)
end
end
for _, poseObj in pairs(poseKeyframe:GetPoses()) do
recurApplyPoses(nil, poseObj)
end
end
if useR15 then
-- Build R15 rig
local humanoidRootPart = mannequin:WaitForChild("HumanoidRootPart")
humanoidRootPart.CFrame = CFrame.new(Vector3.new(0, 5, 0)) * CFrame.Angles(0, math.pi, 0)
humanoidRootPart.Anchored = true
buildRigFromAttachments(humanoidRootPart)
if tool then
applyR15ToolPose(mannequin)
local hand = mannequin:FindFirstChild("RightHand")
local handle = tool:FindFirstChild("Handle")
if hand and handle then
local handGrip = hand:FindFirstChild("RightGripAttachment")
if handGrip then
handle.CFrame = hand.CFrame * handGrip.CFrame * tool.Grip:inverse()
end
end
tool.Parent = mannequin
elseif poseAnimationId then
applyPoseToCharacter(mannequin, poseAnimationId)
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)
@@ -0,0 +1,15 @@
-- Pants v1.0.1
assetUrl, fileExtension, x, y, baseUrl, mannequinId = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ScriptContext").ScriptsDisabled = true
local mannequin = game:GetObjects(baseUrl.. "/asset/?id=" .. tostring(mannequinId))[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)
@@ -0,0 +1,19 @@
-- Place v1.0.2
assetUrl, fileExtension, x, y, baseUrl, universeId = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
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)
@@ -0,0 +1,21 @@
-- Place Validation v1.0.0
assetUrl, baseUrl = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ScriptContext").ScriptsDisabled = true
game:GetService("StarterGui").ShowDevelopmentGui = false
local success, message = pcall(function()
game:Load(assetUrl)
end)
if not success then
return message
end
-- 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 true
@@ -0,0 +1,15 @@
-- Shirt v1.0.1
assetUrl, fileExtension, x, y, baseUrl, mannequinId = ...
pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl) end)
game:GetService("ScriptContext").ScriptsDisabled = true
local mannequin = game:GetObjects(baseUrl.. "/asset/?id=" .. tostring(mannequinId))[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)