572 lines
17 KiB
JavaScript
572 lines
17 KiB
JavaScript
const express = require("express")
|
|
const router = express.Router()
|
|
const signatures = require("./signatures.js")
|
|
const fetch = (...args) =>
|
|
import("node-fetch").then(({ default: fetch }) => fetch(...args))
|
|
const User = require("../model/user.js")
|
|
const { requireAuth } = require("../middleware/authmiddlewaregame")
|
|
const rcctalk = require("../rcctalk")
|
|
const games = require("../model/games.js")
|
|
const catalog = require("../model/item.js")
|
|
const rcc = require("../model/rcc.js")
|
|
var sanitize = require("mongo-sanitize")
|
|
const {
|
|
getPort,
|
|
checkPort,
|
|
getRandomPort,
|
|
waitForPort,
|
|
} = require("get-port-please")
|
|
const fs = require("fs")
|
|
const gamescript = fs.readFileSync("actualgameserver.lua", "utf-8")
|
|
require("dotenv").config()
|
|
const RCC_HOST = process.env.RCC_HOST
|
|
const logshook = process.env.logshook
|
|
const crypto = require("crypto")
|
|
const key = fs.readFileSync("DefaultPrivateKey.pem")
|
|
const key2 = fs.readFileSync("DefaultPrivateKey.pem")
|
|
const key2020 = fs.readFileSync("PrivateKey2020.txt")
|
|
const { _2020placelauncher } = require("../routes/2020/game")
|
|
const { _2018placelauncher } = require("../routes/2018/game")
|
|
|
|
//join and placelauncher
|
|
function sleep(ms) {
|
|
return new Promise(resolve => {
|
|
setTimeout(resolve, ms)
|
|
})
|
|
}
|
|
|
|
router.get("/visit", async (req, res) => {
|
|
// studio
|
|
const string = `local RS = game:GetService("RunService")
|
|
local P = game:GetService("Players")
|
|
local LP = P:CreateLocalPlayer(0)
|
|
LP.CharacterAppearance = ""
|
|
LP.CharacterAdded:connect(
|
|
function(c)
|
|
repeat
|
|
wait()
|
|
until c:FindFirstChild("Humanoid")
|
|
local h = c:FindFirstChild("Humanoid")
|
|
h.Died:connect(
|
|
function()
|
|
wait(5)
|
|
LP:LoadCharacter()
|
|
end
|
|
)
|
|
end
|
|
)
|
|
game:GetService("InsertService"):SetBaseSetsUrl("http://mete0r.xyz/Game/Tools/InsertAsset.ashx?nsets=10&type=base")
|
|
game:GetService("InsertService"):SetUserSetsUrl("http://mete0r.xyz/Game/Tools/InsertAsset.ashx?nsets=20&type=user&userid=%d")
|
|
game:GetService("InsertService"):SetCollectionUrl("http://mete0r.xyz/Game/Tools/InsertAsset.ashx?sid=%d")
|
|
pcall(function() game:GetService("InsertService"):SetFreeModelUrl("http://mete0r.xyz/Game/Tools/InsertAsset.ashx?type=fm&q=%s&pg=%d&rs=%d") end)
|
|
pcall(function() game:GetService("InsertService"):SetFreeDecalUrl("http://mete0r.xyz/Game/Tools/InsertAsset.ashx?type=fd&q=%s&pg=%d&rs=%d") end)
|
|
RS:Run()
|
|
LP:LoadCharacter()
|
|
pcall(
|
|
function()
|
|
game:GetService("ContentProvider"):SetBaseUrl("http://mete0r.xyz" .. "/")
|
|
end
|
|
)
|
|
|
|
|
|
|
|
`
|
|
const sign = crypto.createSign("SHA1")
|
|
sign.update("\r\n" + string)
|
|
var signature = sign.sign(key, "base64")
|
|
|
|
res.send("--rbxsig%" + signature + "%\r\n" + string)
|
|
})
|
|
|
|
router.get(["/GetCurrentUser", "/GetCurrentUser.ashx"], async (req, res) => {
|
|
res.send("1") // 1 means logged in and null means logged out
|
|
}) // don't send 404 error but i don't think we will have studio publishing
|
|
|
|
router.post("/validate-machine", async (req, res) => {
|
|
res.json({ success: true, message: "" })
|
|
})
|
|
|
|
router.get(["/join", "/join.ashx"], requireAuth, async (req, res) => {
|
|
if (!req.userdocument.discordid) {
|
|
return res.json({
|
|
status: "error",
|
|
error: "Link your discord account stinky",
|
|
})
|
|
}
|
|
if (req.query.ver === "2018") {
|
|
if (
|
|
!req.userdocument.gamejoin2018 ||
|
|
req.userdocument.gamejoin2018 === "{}"
|
|
) {
|
|
return res.json({ status: "error", error: "no placelauncher" })
|
|
}
|
|
var joinJson = JSON.parse(req.userdocument.gamejoin2018)
|
|
req.userdocument.gamejoin2018 = undefined
|
|
req.userdocument.markModified("gamejoin2018")
|
|
await req.userdocument.save()
|
|
//sign with our sign module
|
|
var signature = signatures.signer(joinJson)
|
|
//console.log(signature)
|
|
|
|
return res.send(
|
|
"--rbxsig%" + signature + "%\r\n" + JSON.stringify(joinJson),
|
|
)
|
|
}
|
|
if (req.query.ver === "2020") {
|
|
if (
|
|
!req.userdocument.gamejoin2020 ||
|
|
req.userdocument.gamejoin2020 === "{}"
|
|
) {
|
|
return res.json({ status: "error", error: "no placelauncher" })
|
|
}
|
|
var joinJson = JSON.parse(req.userdocument.gamejoin2020)
|
|
req.userdocument.gamejoin2020 = undefined
|
|
req.userdocument.markModified("gamejoin2020")
|
|
await req.userdocument.save()
|
|
//sign with our sign module
|
|
const sign = crypto.createSign("SHA1")
|
|
sign.update("\r\n" + JSON.stringify(joinJson))
|
|
var signature = sign.sign(key2020, "base64")
|
|
|
|
//console.log(signature)
|
|
|
|
return res.send(
|
|
"--rbxsig2%" + signature + "%\r\n" + JSON.stringify(joinJson),
|
|
)
|
|
}
|
|
if (!req.userdocument.gamejoin || req.userdocument.gamejoin === "{}") {
|
|
return res.json({ status: "error", error: "no placelauncher" })
|
|
}
|
|
var joinJson = JSON.parse(req.userdocument.gamejoin)
|
|
req.userdocument.gamejoin = undefined
|
|
req.userdocument.markModified("gamejoin")
|
|
await req.userdocument.save()
|
|
//sign with our sign module
|
|
var signature = signatures.signer(joinJson)
|
|
//console.log(signature)
|
|
|
|
res.send("--rbxsig%" + signature + "%\r\n" + JSON.stringify(joinJson))
|
|
})
|
|
|
|
router.all(
|
|
["/placelauncher", "/placelauncher.ashx"],
|
|
requireAuth,
|
|
_2020placelauncher,
|
|
_2018placelauncher,
|
|
async (req, res, next) => {
|
|
var enabled = req.config
|
|
if (enabled.GamesEnabled === false) {
|
|
return res.json({
|
|
status: "error",
|
|
error: "Games are disabled bad boy",
|
|
})
|
|
}
|
|
var joinJson = {
|
|
ClientPort: 0,
|
|
MachineAddress: "localhost",
|
|
ServerPort: 25564,
|
|
PingUrl: "",
|
|
PingInterval: 120,
|
|
UserName: "default",
|
|
SeleniumTestMode: false,
|
|
UserId: 0,
|
|
SuperSafeChat: false,
|
|
CharacterAppearance:
|
|
"http://shitncumblox.gq/game/charapp?name=default",
|
|
ClientTicket: "",
|
|
GameId: 1,
|
|
PlaceId: 1818,
|
|
MeasurementUrl: "",
|
|
WaitingForCharacterGuid: "cad99b30-7983-434b-b24c-eac12595e5fd",
|
|
BaseUrl: "http://www.mete0r.xyz/",
|
|
ChatStyle: "ClassicAndBubble",
|
|
VendorId: 0,
|
|
ScreenShotInfo: "",
|
|
VideoInfo:
|
|
'<?xml version="1.0"?><entry xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:yt="http://gdata.youtube.com/schemas/2007"><media:group><media:title type="plain"><![CDATA[ROBLOX Place]]></media:title><media:description type="plain"><![CDATA[ For more games visit http://www.roblox.com]]></media:description><media:category scheme="http://gdata.youtube.com/schemas/2007/categories.cat">Games</media:category><media:keywords>ROBLOX, video, free game, online virtual world</media:keywords></media:group></entry>',
|
|
CreatorId: 0,
|
|
CreatorTypeEnum: "User",
|
|
MembershipType: "None",
|
|
AccountAge: 365,
|
|
CookieStoreFirstTimePlayKey: "rbx_evt_ftp",
|
|
CookieStoreFiveMinutePlayKey: "rbx_evt_fmp",
|
|
CookieStoreEnabled: true,
|
|
IsRobloxPlace: false,
|
|
GenerateTeleportJoin: false,
|
|
IsUnknownOrUnder13: false,
|
|
SessionId:
|
|
"c25fd620-bbaa-4fb2-b022-3f053cdd1abd|00000000-0000-0000-0000-000000000000|0|204.236.226.210|8|2016-08-17T01:05:05.7115837Z|0|null|null|null|null",
|
|
DataCenterId: 0,
|
|
UniverseId: 0,
|
|
BrowserTrackerId: 0,
|
|
UsePortraitMode: false,
|
|
FollowUserId: 0,
|
|
CharacterAppearanceId: 1,
|
|
}
|
|
if (!req.query.name && !req.query.placeId) {
|
|
return res.json({ status: "error", error: "no placeid bad" })
|
|
}
|
|
if (req.userdocument.gamejoin) {
|
|
return res.json({
|
|
jobId: "Test",
|
|
status: 2,
|
|
joinScriptUrl:
|
|
"http://mete0r.xyz/game/join.ashx?auth=" + req.query.auth ??
|
|
req.cookies.jwt,
|
|
authenticationUrl: "http://mete0r.xyz/Login/Negotiate.ashx",
|
|
authenticationTicket: "SomeTicketThatDosentCrash",
|
|
message: "",
|
|
})
|
|
}
|
|
var sanitizedplaceid = sanitize(req.query.name ?? req.query.placeId)
|
|
const game = await games.findOne({ idofgame: sanitizedplaceid }).lean()
|
|
if (!game) {
|
|
return res.json({
|
|
status: "error",
|
|
error: "that game doesn't exist!",
|
|
})
|
|
}
|
|
if (game.version != "2016") {
|
|
return res.json({
|
|
status: "error",
|
|
error: "game version is different than client requested",
|
|
})
|
|
}
|
|
const instance = await rcc.findOne({ PlaceId: sanitizedplaceid }).lean()
|
|
if (instance && instance.Status === 2) {
|
|
// if an rcc instance already exists we don't need to create a new one so we will just drag them into the existing game
|
|
joinJson.UserName = req.userdocument.username
|
|
joinJson.UserId = req.userdocument.userid
|
|
joinJson.CharacterAppearance =
|
|
"http://mete0r.xyz/game/charapp?name=" + req.userdocument.userid
|
|
joinJson.MachineAddress = RCC_HOST // need to put rcc host here lol
|
|
joinJson.ServerPort = instance.Port
|
|
joinJson.PlaceId = instance.PlaceId
|
|
joinJson.GameId = sanitizedplaceid
|
|
joinJson.CharacterAppearanceId = req.userdocument.userid
|
|
joinJson.MembershipType = req.userdocument.membership
|
|
joinJson.CreatorId = game.useridofowner
|
|
joinJson.SessionId = req.query.auth ?? req.cookies.jwt
|
|
|
|
const timestamp = Date.now()
|
|
joinJson.ClientTicket = timestamp + ";" // timestamp
|
|
//create signature 1
|
|
const sign1 = crypto.createSign("SHA1")
|
|
sign1.update(
|
|
`${req.userdocument.userid}\n` /*userid*/ +
|
|
`${req.userdocument.username}\n` /*username*/ +
|
|
`http://mete0r.xyz/game/charapp?name=${req.userdocument.userid}\n` /*charapp*/ +
|
|
`game${sanitizedplaceid}\n` /*jobid*/ +
|
|
timestamp /*timestamp*/,
|
|
)
|
|
var signature1 = sign1.sign(key, "base64")
|
|
joinJson.ClientTicket += signature1 + ";"
|
|
|
|
//create signature 2
|
|
const sign2 = crypto.createSign("SHA1")
|
|
sign2.update(
|
|
`${req.userdocument.userid}\n` /*userid*/ +
|
|
`game${sanitizedplaceid}\n` /*jobid*/ +
|
|
timestamp /*timestamp*/,
|
|
)
|
|
var signature2 = sign2.sign(key, "base64")
|
|
joinJson.ClientTicket += signature2
|
|
|
|
req.userdocument.gamejoin = JSON.stringify(joinJson)
|
|
req.userdocument.markModified("gamejoin")
|
|
await req.userdocument.save()
|
|
var joinScriptJson = {
|
|
jobId: "Test",
|
|
status: 2,
|
|
joinScriptUrl:
|
|
"http://mete0r.xyz/game/join.ashx?auth=" +
|
|
joinJson.SessionId,
|
|
authenticationUrl: "http://mete0r.xyz/Login/Negotiate.ashx",
|
|
authenticationTicket: "SomeTicketThatDosentCrash",
|
|
message: "",
|
|
}
|
|
|
|
return res.send(JSON.stringify(joinScriptJson))
|
|
}
|
|
if (instance && instance.Status === 1) {
|
|
var joinScriptJson = {
|
|
jobId: "Test",
|
|
status: 1,
|
|
joinScriptUrl:
|
|
"http://mete0r.xyz/game/join.ashx?auth=" +
|
|
joinJson.SessionId,
|
|
authenticationUrl: "http://mete0r.xyz/Login/Negotiate.ashx",
|
|
authenticationTicket: "SomeTicketThatDosentCrash",
|
|
message: "",
|
|
}
|
|
return res.send(JSON.stringify(joinScriptJson))
|
|
}
|
|
|
|
var port = await getPort({ random: true })
|
|
var newgamescript =
|
|
"local placeId = " + sanitizedplaceid + "\n" + gamescript
|
|
newgamescript = "local port = " + port + "\n" + newgamescript
|
|
// launch job
|
|
var response = await rcctalk.OpenJob(
|
|
"game" + sanitizedplaceid,
|
|
newgamescript,
|
|
"99999",
|
|
)
|
|
await rcc.create({
|
|
PlaceId: sanitizedplaceid,
|
|
Port: port,
|
|
Status: 1, // 1 means loading
|
|
})
|
|
|
|
//console.log(newrenderscript)
|
|
|
|
var joinScriptJson = {
|
|
jobId: "Test",
|
|
status: 1,
|
|
joinScriptUrl:
|
|
"http://mete0r.xyz/game/join.ashx?auth=" + joinJson.SessionId,
|
|
authenticationUrl: "http://mete0r.xyz/Login/Negotiate.ashx",
|
|
authenticationTicket: "SomeTicketThatDosentCrash",
|
|
message: "",
|
|
}
|
|
res.send(JSON.stringify(joinScriptJson))
|
|
},
|
|
)
|
|
|
|
//charapp and colors stealing from roblox
|
|
|
|
router.get("/charapp", async (req, res) => {
|
|
if (Object.keys(req.query).length === 0) {
|
|
res.status(404).send("No variables :(")
|
|
} else {
|
|
const user = await User.findOne({ userid: req.query.name }).lean()
|
|
const placeid = req.headers?.["roblox-place-id"] ?? 0
|
|
const placedoc = await games.findOne({ idofgame: placeid })
|
|
if (!placedoc) {
|
|
return res.json({ status: "error", error: "Place not found." })
|
|
}
|
|
|
|
if (!user) {
|
|
return res.json({ status: "error", error: "User not found!" })
|
|
}
|
|
|
|
if (!user.inventory) {
|
|
if (req.query.rcc) {
|
|
return res.json([])
|
|
}
|
|
return res.send(
|
|
"http://mete0r.xyz/game/colors?name=" + req.query.name + ";",
|
|
)
|
|
}
|
|
|
|
if (req.query.rcc) {
|
|
var empty = []
|
|
for (var key of user.inventory) {
|
|
if (key.Equipped === true) {
|
|
empty.push({ item: { itemid: key.ItemId, type: key.Type } })
|
|
}
|
|
}
|
|
return res.json(empty)
|
|
}
|
|
|
|
var charapp = "http://mete0r.xyz/asset?name=" + req.query.name + ";"
|
|
// add to charapp string by adding json to it
|
|
for (var key of user.inventory) {
|
|
if (key.Equipped === true) {
|
|
if (placedoc.gearallowed ?? false === true) {
|
|
charapp += "http://mete0r.xyz/asset?id=" + key.ItemId + ";"
|
|
} else {
|
|
if (key.Type != "Gears") {
|
|
charapp +=
|
|
"http://mete0r.xyz/asset?id=" + key.ItemId + ";"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
res.write(charapp)
|
|
res.end()
|
|
}
|
|
})
|
|
|
|
router.get("/colors", async (req, res) => {
|
|
if (Object.keys(req.query).length === 0) {
|
|
res.status(404).send("No variables :(")
|
|
} else {
|
|
const user = await User.findOne({ userid: req.query.name }).lean()
|
|
if (!user) {
|
|
return res.json({ status: "error", error: "User not found!" })
|
|
}
|
|
|
|
if (req.query.rcc) {
|
|
var empty = []
|
|
for (var key of user.colors) {
|
|
empty.push(key.value)
|
|
}
|
|
return res.json(empty)
|
|
}
|
|
|
|
res.type("application/xml")
|
|
var colorsxml =
|
|
`<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" version="4">
|
|
<External>null</External>
|
|
<External>nil</External>
|
|
<Item class="BodyColors">
|
|
<Properties>
|
|
<int name="HeadColor">` +
|
|
user.colors.find(x => x.name === "Head").value +
|
|
`</int>
|
|
<int name="LeftArmColor">` +
|
|
user.colors.find(x => x.name === "Left Arm").value +
|
|
`</int>
|
|
<int name="LeftLegColor">` +
|
|
user.colors.find(x => x.name === "Left Leg").value +
|
|
`</int>
|
|
<string name="Name">Body Colors</string>
|
|
<int name="RightArmColor">` +
|
|
user.colors.find(x => x.name === "Right Arm").value +
|
|
`</int>
|
|
<int name="RightLegColor">` +
|
|
user.colors.find(x => x.name === "Right Leg").value +
|
|
`</int>
|
|
<int name="TorsoColor">` +
|
|
user.colors.find(x => x.name === "Torso").value +
|
|
`</int>
|
|
<bool name="archivable">true</bool>
|
|
</Properties>
|
|
</Item>
|
|
</roblox>`
|
|
|
|
res.send(colorsxml)
|
|
}
|
|
})
|
|
|
|
router.get("/", (req, res) => {
|
|
res.status(404).send("hmmm? kinda sus")
|
|
})
|
|
|
|
router.get("/players/:id", (req, res) => {
|
|
res.json({ ChatFilter: "whitelist" })
|
|
})
|
|
|
|
router.post("/load-place-info", (req, res) => {
|
|
res.json({ CreatorId: 0, CreatorType: "User", PlaceVersion: 1 })
|
|
})
|
|
|
|
router.post("/badge/awardbadge", async (req, res) => {
|
|
const userid = req.query.UserID
|
|
const badgeid = req.query.BadgeID
|
|
const placeid = req.query.PlaceID
|
|
|
|
const badge = await catalog.findOne({ ItemId: badgeid }).lean()
|
|
const user = await User.findOne({ userid: userid }).lean()
|
|
|
|
if (!badge) {
|
|
//Badge doesn't exist!
|
|
return res.send("0")
|
|
}
|
|
|
|
if (!user) {
|
|
return res.send("0")
|
|
}
|
|
|
|
const badgecreator = await User.findOne({ userid: badge.Creator }).lean()
|
|
|
|
if (typeof user.badges !== "undefined") {
|
|
// check if user already owns item
|
|
for (var v of user.badges) {
|
|
if (v.badgeid === badgeid) {
|
|
// they already own it
|
|
return res.send("0")
|
|
}
|
|
}
|
|
}
|
|
|
|
User.updateOne(
|
|
{ userid: req.query.UserID },
|
|
{
|
|
$push: {
|
|
badges: {
|
|
badgeid: badgeid,
|
|
badgename: badge.Name,
|
|
creator: badge.Creator,
|
|
placeid: placeid,
|
|
},
|
|
},
|
|
},
|
|
function (err, doc) {
|
|
if (err) {
|
|
return res.send("0")
|
|
}
|
|
},
|
|
)
|
|
|
|
return res.send(
|
|
user.username +
|
|
" won " +
|
|
badgecreator.username +
|
|
"'s " +
|
|
badge.Name +
|
|
" award!",
|
|
)
|
|
})
|
|
|
|
router.get(
|
|
[
|
|
"/LuaWebService/HandleSocialRequest",
|
|
"/LuaWebService/HandleSocialRequest.ashx",
|
|
],
|
|
async (req, res) => {
|
|
res.type("application/xml")
|
|
if (req.query.method === "IsInGroup") {
|
|
if (req.query.groupid === "0" || req.query.groupid === "1200769") {
|
|
// 1200769 admin group
|
|
const user = await User.findOne({
|
|
userid: req.query.playerid,
|
|
}).lean()
|
|
if (user) {
|
|
return res.send(
|
|
`<Value Type="boolean">${user.admin}</Value>`,
|
|
)
|
|
}
|
|
}
|
|
return res.send('<Value Type="boolean">false</Value>')
|
|
}
|
|
if (req.query.method === "GetGroupRank") {
|
|
if (req.query.groupid === "0" || req.query.groupid === "1200769") {
|
|
const user = await User.findOne({
|
|
userid: req.query.playerid,
|
|
}).lean()
|
|
if (user) {
|
|
if (user.admin === true) {
|
|
return res.send(`<Value Type="integer">255</Value>`)
|
|
}
|
|
}
|
|
}
|
|
return res.send('<Value Type="integer">0</Value>')
|
|
}
|
|
if (req.query.method === "IsBestFriendsWith") {
|
|
return res.send('<Value Type="boolean">false</Value>')
|
|
}
|
|
if (req.query.method === "IsFriendsWith") {
|
|
return res.send('<Value Type="boolean">false</Value>')
|
|
}
|
|
res.type("html")
|
|
return res.status(404).end()
|
|
},
|
|
)
|
|
|
|
router.get("/Tools/InsertAsset.ashx", async (req, res) => {
|
|
const lol = await fetch("http://sets.pizzaboxer.xyz/Game" + req.url)
|
|
if (lol.status === 400) {
|
|
return res.send(``)
|
|
}
|
|
return res.send(await lol.text())
|
|
})
|
|
|
|
router.post("/MachineConfiguration.ashx", (req, res) => {
|
|
res.json({ success: true })
|
|
})
|
|
|
|
module.exports = router
|