Created
June 22, 2024 21:58
-
-
Save Heliodex/639fea9aa7808a7d9cf9c1ed4cfc4eff to your computer and use it in GitHub Desktop.
Mercury RCCService proxy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// "GO? | |
// WHY THE FUCK | |
// IS THIS IN GO? | |
// ARE YOU STUPID | |
// ???" | |
// - taskmanager, 9 January 2024 | |
// cope harder | |
// don't forget da .env | |
package main | |
import ( | |
"compress/gzip" | |
"embed" | |
"encoding/base64" | |
"fmt" | |
"io" | |
"net/http" | |
"os" | |
"os/exec" | |
"strings" | |
"sync" | |
"time" | |
c "github.com/TwiN/go-color" | |
"github.com/disintegration/imaging" | |
env "github.com/joho/godotenv" | |
) | |
var ( | |
_ embed.FS // force embed import hack | |
wg sync.WaitGroup | |
client http.Client | |
//go:embed soap.xml | |
template string | |
) | |
func Log(txt string) { | |
// I HATE GO DATE FORMATTING!!! I HATE GO DATE FORMATTING!!! | |
fmt.Println(time.Now().Format("2006/01/02, 15:04:05 "), txt) | |
} | |
func Logr(txt string) { | |
fmt.Print("\r", time.Now().Format("2006/01/02, 15:04:05 "), txt) // fmt.Print don't add spaces between args | |
} | |
func Assert(err error, txt string) { | |
// so that I don't have to write this every time | |
// todo: should only be used for fatal errors | |
if err != nil { | |
fmt.Println(err) | |
Log(c.InRed(txt)) | |
os.Exit(1) | |
} | |
} | |
func StartRCC() { | |
_, err := os.Stat("./RCCService/RCCService.exe") | |
Assert(err, "RCCService.exe not found! Please place the RCCService folder in the current directory.") | |
for { | |
exec.Command("./RCCService/RCCService.exe", "-Console").Run() | |
Log(c.InRed("RCCService has stopped. Restarting...")) | |
} | |
} | |
func TestRCCStarted(loaded *bool, t int) { | |
Logr(c.InPurple(fmt.Sprintf("Waiting for RCCService to start... (%ds)", t))) | |
_, err := http.Get("http://localhost:64989") | |
if err == nil { | |
*loaded = true | |
} | |
} | |
func Compress(b64 string, resolution int, name string, compressed *string) { | |
data, err := base64.StdEncoding.DecodeString(b64) | |
Assert(err, "Failed to decode base64 of image") | |
srcimg, err := imaging.Decode(strings.NewReader(string(data))) | |
Assert(err, "Failed to decode image from data") | |
// Lanczos my beloved 💖 (change it to something faster idc) | |
img := imaging.Resize(srcimg, resolution, resolution, imaging.Lanczos) | |
Assert(err, "Failed to create image from data") | |
writer := new(strings.Builder) | |
imaging.Encode(writer, img, imaging.PNG) | |
*compressed = base64.StdEncoding.EncodeToString([]byte(writer.String())) | |
wg.Done() | |
} | |
func idRoute(w http.ResponseWriter, r *http.Request) { | |
// remove port from IP (can't just split by ":" because of IPv6) | |
if ip := r.RemoteAddr[:strings.LastIndex(r.RemoteAddr, ":")]; ip != os.Getenv("IP") && ip != "[::1]" { | |
Log(c.InRed("IP " + ip + " is not allowed! (render)")) | |
w.WriteHeader(http.StatusForbidden) | |
return | |
} | |
loadScript, err := io.ReadAll(r.Body) | |
Assert(err, "Failed to read render script") | |
script := strings.ReplaceAll(string(loadScript), "_PING_URL", "http://localhost:64990/ping") | |
id := r.PathValue("id") | |
currentTemplate := strings.ReplaceAll(template, "_TASK_ID", id) | |
currentTemplate = strings.ReplaceAll(currentTemplate, "_RENDER_SCRIPT", script) | |
req, err := http.NewRequest("POST", "http://localhost:64989", strings.NewReader(currentTemplate)) | |
Assert(err, "Failed to create request") | |
req.Header.Set("Content-Type", "text/xml; charset=utf-8") | |
req.Header.Set("SOAPAction", "http://roblox.com/OpenJobEx") | |
Log(c.InBlue("Sending request to render " + id)) | |
client.Do(req) | |
w.WriteHeader(http.StatusOK) | |
} | |
func pingIdRoute(w http.ResponseWriter, r *http.Request) { | |
// remove port from IP (can't just split by ":" because of IPv6) | |
if ip := r.RemoteAddr[:strings.LastIndex(r.RemoteAddr, ":")]; ip != "[::1]" { | |
Log(c.InRed("IP " + ip + " is not allowed! (ping)")) | |
w.WriteHeader(http.StatusForbidden) | |
return | |
} | |
apiKey := r.URL.Query().Get("apiKey") | |
id := r.PathValue("id") | |
readBody, err := io.ReadAll(r.Body) | |
Assert(err, "Failed to read request body") | |
// if the body is gzipped, unzip it | |
if strings.HasPrefix(string(readBody), "\x1f\x8b") { | |
reader, err := gzip.NewReader(strings.NewReader(string(readBody))) | |
Assert(err, "Failed to create gzip reader") | |
readBody, err = io.ReadAll(reader) | |
Assert(err, "Failed to read gzipped request body") | |
} | |
data := strings.Split(string(readBody), "\n") | |
status := data[0] | |
var compressed []string | |
switch status { | |
case "Rendering": | |
Log(c.InGreen("Render " + id + " is rendering")) | |
case "Completed": | |
num := len(data) - 1 | |
wg.Add(num) | |
// Could result in a random order if appending to an array instead | |
var body, head string | |
go Compress(data[1], 420, "body", &body) | |
if num == 2 { | |
go Compress(data[2], 150, "head", &head) | |
} | |
wg.Wait() | |
compressed = []string{body} | |
if num == 2 { | |
compressed = append(compressed, head) | |
} | |
Log(c.InGreen("Render " + id + " is complete")) | |
} | |
compressed = append([]string{status}, compressed...) | |
// Send to server as base64 | |
// todo make it multipart/form-data or something for lower bandwidth | |
endpoint := fmt.Sprintf("%s/%s?apiKey=%s", os.Getenv("ENDPOINT"), id, apiKey) | |
// We (still) have to lie about the contentType to avoid being nuked by CORS from the website | |
_, err = http.Post(endpoint, "text/json", strings.NewReader(strings.Join(compressed, "\n"))) | |
Assert(err, "Failed to send render data to server") | |
w.WriteHeader(http.StatusOK) | |
} | |
func main() { | |
Log(c.InYellow("Loading environment variables...")) | |
err := env.Load(".env") | |
Assert(err, "Failed to load environment variables. Please place them in a .env file in the current directory.") | |
Log(c.InPurple("Starting RCCService...")) | |
go StartRCC() | |
loaded := false | |
for startTime := time.Now(); !loaded; time.Sleep(100 * time.Millisecond) { | |
go TestRCCStarted(&loaded, int(time.Since(startTime).Seconds())) | |
} | |
fmt.Println() | |
Log(c.InPurple("Starting server...")) | |
router := http.NewServeMux() | |
router.HandleFunc("POST /{id}", idRoute) | |
router.HandleFunc("POST /ping/{id}", pingIdRoute) | |
Log(c.InGreen("~ RCCService is up on port 64990 ~")) | |
Log(c.InGreen("Send a POST request to /{your task id} with the render script as the body to start a render")) | |
http.ListenAndServe(":64990", router) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xmlns:xsd="http://www.w3.org/2001/XMLSchema" | |
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> | |
<soap:Body> | |
<BatchJobEx> | |
<job> | |
<id>_TASK_ID</id> | |
<expirationInSeconds>30</expirationInSeconds> | |
<category>1</category> | |
<cores>1</cores> | |
</job> | |
<script> | |
<name>RenderScript</name> | |
<script> | |
_RENDER_SCRIPT | |
</script> | |
</script> | |
</BatchJobEx> | |
</soap:Body> | |
</soap:Envelope> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment