Skip to content

Instantly share code, notes, and snippets.

@nosoop
Last active September 22, 2017 15:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nosoop/57fa25e21d027f1afc8c797131344561 to your computer and use it in GitHub Desktop.
Save nosoop/57fa25e21d027f1afc8c797131344561 to your computer and use it in GitHub Desktop.
workaround for that funky MOTD caching issue
/**
* VGUIMenu Cached Domain Workaround
*
* Abuse MOTDs a lot on one domain name? Lately there's been a bug (?) that causes the web view
* to navigate to a last known page that isn't what's being called for.
*
* See: https://forums.alliedmods.net/showpost.php?p=2529760&postcount=69
*/
#pragma semicolon 1
#include <sourcemod>
#include <regex>
#pragma newdecls required
#define PLUGIN_VERSION "0.0.0"
public Plugin myinfo = {
name = "[TF2] VGUIMenu Cache Workaround",
author = "nosoop",
description = "Bypasses some cache weirdness causing issues with MOTDs on one domain.",
version = PLUGIN_VERSION,
url = "https://localhost/"
}
/**
* URL to your copy of the MOTD frame proxy page. It can be any valid HTTP / HTTPS URL with
* paths. Don't include the hash character here; that's done during `OnVGUIMenuPreSent`.
*
* It's preferred that it points to a second-level domain name that you're not using for your
* MOTD, as MOTDs aren't automatically redirected (though you could modify your `motdfile` to
* run through the MOTD proxy URL.
*/
#define MOTD_PROXY_URL "http://motdproxy.us.to/"
public void OnPluginStart() {
UserMsg vguiMessage = GetUserMessageId("VGUIMenu");
HookUserMessage(vguiMessage, OnVGUIMenuPreSent, true);
}
/**
* Intercepts VGUIMenu messages, including ones created by ShowMOTDPanel and variants.
*/
public Action OnVGUIMenuPreSent(UserMsg vguiMessage, BfRead buffer, const int[] players,
int nPlayers, bool reliable, bool init) {
// compile a regular expression to bypass group URLs
static Regex s_SteamGroupExpr;
if (!s_SteamGroupExpr) {
s_SteamGroupExpr = new Regex("https?:\\/\\/steamcommunity.com\\/gid\\/");
}
// implementation based on CHalfLife2::ShowVGUIMenu in sourcemod/core/HalfLife2.cpp
char name[128];
buffer.ReadString(name, sizeof(name));
if (StrEqual(name, "info")) {
DataPack dataBuffer = new DataPack();
dataBuffer.WriteCell(nPlayers);
for (int i = 0; i < nPlayers; i++) {
dataBuffer.WriteCell(players[i]);
}
int flags = (reliable? USERMSG_RELIABLE : 0) | (init? USERMSG_INITMSG : 0);
dataBuffer.WriteCell(flags);
dataBuffer.WriteCell(buffer.ReadByte()); // bool bShow
int count = buffer.ReadByte();
dataBuffer.WriteCell(count); // int count;
// determines if the usermessage is for a web page (key "msg", value /^http/)
bool bProxiedPage;
// count is for key-value pairs
for (int i = 0; i < count; i++) {
char key[256], value[1024];
buffer.ReadString(key, sizeof(key), false);
dataBuffer.WriteString(key);
buffer.ReadString(value, sizeof(value), false);
if (StrEqual(key, "msg") && StrContains(value, "http") == 0
&& s_SteamGroupExpr.Match(value) == 0) {
// concat the hooked message's URL as a location hash
// we abuse the fact that the page navigation still follows hashes
// location hashes aren't sent with these requests so you should be OK, but you
// do want to verify that the proxy page isn't doing anything shady too
char newURL[1024] = MOTD_PROXY_URL ... "#";
StrCat(newURL, sizeof(newURL), value);
dataBuffer.WriteString(newURL);
// TODO rearchitecture this by only copying the buffer contents
// so we can track and send differing domains to each client if necessary
bProxiedPage = true;
} else {
dataBuffer.WriteString(value);
}
}
if (bProxiedPage) {
RequestFrame(SendDataPackVGUI, dataBuffer);
return Plugin_Handled;
} else {
delete dataBuffer;
}
}
return Plugin_Continue;
}
public void SendDataPackVGUI(DataPack dataBuffer) {
dataBuffer.Reset();
int nPlayers = dataBuffer.ReadCell();
int[] players = new int[nPlayers];
for (int i = 0; i < nPlayers; i++) {
players[i] = dataBuffer.ReadCell();
}
int flags = dataBuffer.ReadCell();
BfWrite buffer = view_as<BfWrite>(StartMessage("VGUIMenu", players, nPlayers,
flags | USERMSG_BLOCKHOOKS));
buffer.WriteString("info");
buffer.WriteByte(dataBuffer.ReadCell()); // bShow
int count = dataBuffer.ReadCell();
buffer.WriteByte(count);
char content[1024];
for (int i = 0; i < count; i++) {
dataBuffer.ReadString(content, sizeof(content));
buffer.WriteString(content);
dataBuffer.ReadString(content, sizeof(content));
buffer.WriteString(content);
}
// writestring "cmd" and "closed_htmlpage" if you want to detect closing every html page
// could be useful by itself
delete dataBuffer;
EndMessage();
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Source Engine Cached Request Bypass</title>
<!-- slap this in somewhere and replace the URL in the plugin -->
<style>
body {
margin: 0;
}
/* https://stackoverflow.com/a/27832759 */
#proxied_content {
display: block;
background: #000;
border: none;
height: 100vh;
width: 100vw;
}
</style>
</head>
<body>
<iframe id="proxied_content"> </iframe>
<script>
/* Pass each page as a hash to this HTML page, Source Engine handles the rest. */
window.onload = window.onhashchange = function() {
var url = decodeURIComponent(window.location.hash.slice(1));
document.querySelector('#proxied_content').setAttribute('src', url);
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment