Meteor-back/routes/game.js

413 lines
18 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