Uploaded Source

This commit is contained in:
LAX1DUDE 2022-04-03 23:43:58 -07:00
parent ab731cbf57
commit e86b9908a2
13 changed files with 810 additions and 1 deletions

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2022 LAX1DUDE
Copyright (c) 2022 Calder Young
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

41
README.md Normal file
View File

@ -0,0 +1,41 @@
# Eaglercraft HTML5 Ping Embed
### This allows you to easily embed your Eaglercraft server's ping, player list, and MOTD in a \<div\> element in an HTML5 document using the same style as the game's multiplayer screen
![Eaglercraft HTML5 Ping Embed](https://i.gyazo.com/3db6a206bca69da3545a6083bc8aa7eb.gif)
### Demo link: [https://g.eags.us/eaglercraft/ping.html](https://g.eags.us/eaglercraft/ping.html)
# How to install:
1. Download 'embed.min.js' and 'icons.png' and upload them to your server
2. Add the script: `<script type="text/javascript" src="embed.min.js"></script>`
3. Create a \<div\> like: `<div id="embed"></div>`
4. In your Javascript, create a new `ServerEmbed` object. Pass the \<div\> object and a CSS width as parameters:
`var embed = new ServerEmbed(document.getElementById("embed"), "500px");`
5. Call the `ping` function on the new object: `embed.ping("127.0.0.1:25565");`
(Replace `127.0.0.1:25565` with the `ip:port` or `ws://` or `wss://` address of your server)
6. You're done
## Extra features:
The `ping` function takes five arguments, four of which are optional:
```javascript
ping(addr, name, forceName, hideAddress, hideCracked)
```
- `addr` Is the `ip:port` or `ws://` or `wss://` address of your server
- `name` *(Optional)* Sets the name to display for the server, if left undefined then the default name displayed for the server is the `server_name:` variable of config.yml of the server being pinged (once the server responds)
- `forceName` *(Optional)* Sets if the `name` parameter should always be shown as the server's name instead of the pinged server's config.yml `server_name:` sent in the response to the ping. Default is false, so by default the optional `name` parameter is only shown until the server responds with it's configured `server_name:`, which is then displayed instead until the ping is sent again
- `hideAddress` *(Optional)* Sets if the second line of the server's MOTD **should not display** the `addr` parameter on the canvas while it is still connecting, and then also if it shouldn't display the `addr` when the destination server's response does not specify a second line in it's MOTD. Default is false
- `hideCracked` *(Optional)* Sets if the warning icon and padlock icon, which indicate if the server uses online mode or not, should be hidden from the embed so nobody can tell if your server is cracked or not. Default is false

724
embed.js Normal file
View File

@ -0,0 +1,724 @@
/*
MIT License
Copyright (c) 2022 Calder Young
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
const CHAR_WIDTHS = [
1,9,9,8,8,8,8,7,9,8,9,9,8,9,9,9,
8,8,8,8,9,9,8,9,8,8,8,8,8,9,9,9,
4,2,5,6,6,6,6,3,5,5,5,6,2,6,2,6,
6,6,6,6,6,6,6,6,6,6,2,2,5,6,5,6,
7,6,6,6,6,6,6,6,6,4,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,4,6,4,6,6,
3,6,6,6,6,6,5,6,6,2,6,5,3,6,6,6,
6,6,6,6,4,6,6,6,6,6,6,5,2,5,7,6,
6,6,6,6,6,6,6,6,6,6,6,4,6,3,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,6,
6,3,6,6,6,6,6,6,6,7,6,6,6,2,6,6,
8,9,9,6,6,6,8,8,6,8,8,8,8,8,6,6,
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
9,9,9,9,9,9,9,9,9,6,9,9,9,5,9,9,
8,7,7,8,7,8,8,8,7,8,8,7,9,9,6,7,
7,7,7,7,9,6,7,8,7,6,6,9,7,6,7,1
];
const CHAR_SHADOW_BRIGHTNESS = 0.247;
const SERVER_ASPECT_RATIO = 38.0 / 256.0;
class ServerEmbed {
constructor(tag, width) {
this.containerTag = tag;
tag.style.width = width;
tag.style.height = Math.floor(tag.clientWidth * SERVER_ASPECT_RATIO) + "px";
tag.style.backgroundColor = "black";
this.canvas = document.createElement("canvas");
this.canvas.width = tag.clientWidth;
this.canvas.height = tag.clientHeight;
tag.appendChild(this.canvas);
this.ctx = this.canvas.getContext("2d");
if(!this.ctx) {
throw new Error("CanvasRenderingContext2D is not supported in this browser");
}
this.ctx.imageSmoothingEnabled = false;
this.tmpCanvas = document.createElement("canvas");
this.tmpCanvas.width = 64;
this.tmpCanvas.height = 64;
this.tmpCtx = this.tmpCanvas.getContext("2d");
this.tmpCtx.imageSmoothingEnabled = false;
const self = this;
this.spriteSheet = document.createElement("img");
this.spriteSheet.addEventListener("load", () => {
self.isSpriteSheetLoaded = true;
setInterval(() => self.redraw(), 50);
});
this.spriteSheet.src = "icons.png";
this.socket = null;
this.connected = false;
this.dirty = false;
this.connecting = 0;
this.currentStatus = {
serverName: "",
serverCracked: false,
pingSentTime: 0,
pingToServer: -1,
motdLine1: "",
motdLine2: "",
onlineCount: 0,
maxCount: 0,
players: []
};
this.showCracked = true;
this.tooltip = "";
this.isShowingTooltip = false;
this.isTooltipEnabled = true;
this.tooltipCanvas = document.createElement("canvas");
this.tooltipCanvas.width = 64;
this.tooltipCanvas.height = 64;
this.tooltipCanvas.style.display = "none";
this.tooltipCanvas.style.position = "absolute";
this.tooltipCanvas.style.zIndex = "100";
this.tooltipCanvas.style.pointerEvents = "none";
this.tooltipCtx = this.tooltipCanvas.getContext("2d");
this.tooltipCtx.imageSmoothingEnabled = false;
document.body.appendChild(this.tooltipCanvas);
this.mouseOver = false;
this.mouseX = 0;
this.mouseY = 0;
this.mousePageX = 0;
this.mousePageY = 0;
var mouseMove = (e) => {
self.mouseOver = true;
self.mouseX = e.offsetX / self.canvas.width * 256.0;
self.mouseY = e.offsetY / self.canvas.height * 38.0;
self.mousePageX = e.pageX;
self.mousePageY = e.pageY;
};
this.canvas.addEventListener("mouseover", mouseMove);
this.canvas.addEventListener("mousemove", mouseMove);
this.canvas.addEventListener("mouseout", (e) => {
self.mouseOver = false;
self.tooltipCanvas.style.display = "none";
});
this.lastWidth = 0;
this.redrawTimer = 0;
}
setShowCracked(b) {
if(b != this.showCracked) {
this.showCracked = b;
this.dirty = true;
}
}
setShowTooltip(b) {
this.isTooltipEnabled = b;
}
getPixelX(x) {
return x * this.canvas.width / 256.0;
}
getPixelY(y) {
return y * this.canvas.height / 38.0;
}
drawIcon(x, y, w, h, dx, dy, dw, dh) {
if(this.isSpriteSheetLoaded) {
this.ctx.drawImage(this.spriteSheet, x + 0.05, y + 0.05, w - 0.1, h - 0.1, this.getPixelX(dx),
this.getPixelY(dy), this.getPixelX(dw||w), this.getPixelY(dh||h));
}
}
drawChar(x, y, ch, italic) {
if(ch >= CHAR_WIDTHS.length) {
ch = 176;
}
if(!italic) {
this.drawIcon(ch % 16 * 8, Math.floor(ch / 16.0) * 8.0, CHAR_WIDTHS[ch], 8, x, y);
}else {
if(this.isSpriteSheetLoaded) {
this.ctx.resetTransform();
this.ctx.scale(this.canvas.width / 256.0, this.canvas.height / 38.0);
this.ctx.translate(x + 1.1, y);
this.ctx.transform(1, 0, -0.35, 1, 0, 0);
this.ctx.drawImage(this.spriteSheet, ch % 16 * 8, Math.floor(ch / 16.0) * 8.0,
CHAR_WIDTHS[ch] * 0.99, 8 * 0.99, 0, 0, CHAR_WIDTHS[ch], 8);
this.ctx.resetTransform();
}
}
return CHAR_WIDTHS[ch];
}
drawColoredChar(x, y, ch, color, italic) {
if(ch >= CHAR_WIDTHS.length) {
ch = 176;
}
if(!this.isSpriteSheetLoaded) {
return CHAR_WIDTHS[ch];
}
this.tmpCtx.globalCompositeOperation = "source-over";
this.tmpCtx.clearRect(0, 0, 8, 8);
this.tmpCtx.imageSmoothingEnabled = false;
this.tmpCtx.drawImage(this.spriteSheet, ch % 16 * 8, Math.floor(ch / 16.0) * 8.0,
CHAR_WIDTHS[ch] * 0.99, 8 * 0.99, 0, 0, CHAR_WIDTHS[ch], 8);
this.tmpCtx.globalCompositeOperation = "source-in";
this.tmpCtx.fillStyle = color;
this.tmpCtx.fillRect(0, 0, CHAR_WIDTHS[ch], 8);
if(!italic) {
this.ctx.drawImage(this.tmpCanvas, 0, 0, CHAR_WIDTHS[ch] * 0.99, 8 * 0.99, this.getPixelX(x),
this.getPixelY(y), this.getPixelX(CHAR_WIDTHS[ch]), this.getPixelY(8));
}else {
this.ctx.resetTransform();
this.ctx.scale(this.canvas.width / 256.0, this.canvas.height / 38.0);
this.ctx.translate(x + 1.1, y);
this.ctx.transform(1, 0, -0.35, 1, 0, 0);
this.ctx.drawImage(this.tmpCanvas, 0, 0, CHAR_WIDTHS[ch] * 0.99, 8 * 0.99, 0, 0, CHAR_WIDTHS[ch], 8);
this.ctx.resetTransform();
}
return CHAR_WIDTHS[ch];
}
drawCharLine(x, y, w, clr) {
this.ctx.fillStyle = clr;
this.ctx.fillRect(this.getPixelX(x), this.getPixelY(y), this.getPixelX(w + 0.02), this.getPixelY(1));
}
makeColor(r, g, b) {
return "rgba(" + Math.floor(r) + "," + Math.floor(g) + "," + Math.floor(b) + ",1.0)";
}
getStringWidth(txt, escapeChar) {
if(!escapeChar) {
escapeChar = 167;
}else {
escapeChar = escapeChar.charCodeAt(0);
}
var j = 0;
for(var i = 0; i < txt.length; ++i) {
var c = txt.charCodeAt(i);
if(c == escapeChar) {
i += 2;
}else {
if(c < CHAR_WIDTHS.length) {
j += CHAR_WIDTHS[c];
}else {
j += 6;
}
}
}
return j;
}
drawString(x, y, txt, r, g, b, shadow, randomSeed, escapeChar) {
var escapeCharChar = escapeChar;
if(!escapeChar) {
escapeChar = 167;
}else {
escapeChar = escapeChar.charCodeAt(0);
}
var startX = x;
var startY = y;
if(shadow) {
++x;
++y;
}
if(!randomSeed) {
randomSeed = 3492589035;
}
var readFormat = false;
var cr = r;
var cg = g;
var cb = b;
var obfuscate = false; var bold = false; var strikethrough = false;
var underline = false; var italic = false;
for(var i = 0; i < txt.length; ++i) {
var c = txt.charCodeAt(i);
if(c == escapeChar) { // color code symbol
readFormat = true;
continue;
}
if(readFormat) {
if(c == 48) { // black (0)
cr = cg = cb = 0;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}else if(c == 49) { // dark_blue (1)
cr = cg = 0;
cb = 170;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}else if(c == 50) { // dark_green (2)
cr = cb = 0;
cg = 170;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}else if(c == 51) { // dark_aqua (3)
cr = 0;
cg = cb = 170;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}else if(c == 52) { // dark_red (4)
cr = 170;
cg = cb = 0;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}else if(c == 53) { // dark_purple (5)
cr = cb = 170;
cg = 0;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}else if(c == 54) { // gold (6)
cr = 255;
cg = 170;
cb = 0;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}else if(c == 55) { // gray (7)
cr = cg = cb = 170;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}else if(c == 56) { // dark_gray (8)
cr = cg = cb = 85;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}else if(c == 57) { // blue (9)
cr = cg = 85;
cb = 255;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}else if(c == 97) { // green (a)
cr = cb = 85;
cg = 255;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}else if(c == 98) { // aqua (b)
cr = 85;
cg = cb = 255;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}else if(c == 99) { // red (c)
cg = cb = 85;
cr = 255;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}else if(c == 100) { // light_purple (d)
cr = cb = 255;
cg = 85;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}else if(c == 101) { // yellow (e)
cr = cg = 255;
cb = 85;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}else if(c == 102) { // white (f)
cr = cg = cb = 255;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}else if(c == 107) { // obfuscated (k)
readFormat = false;
obfuscate = true;
continue;
}else if(c == 108) { // bold (l)
readFormat = false;
bold = true;
continue;
}else if(c == 109) { // strikethrough (m)
readFormat = false;
strikethrough = true;
continue;
}else if(c == 110) { // underline (n)
readFormat = false;
underline = true;
continue;
}else if(c == 111) { // italic (o)
readFormat = false;
italic = true;
continue;
}else if(c == 114) { // reset (r)
cr = r;
cg = g;
cb = b;
readFormat = obfuscate = bold =
strikethrough = underline = italic = false;
continue;
}
readFormat = false;
continue;
}
if(obfuscate) {
this.dirty = true;
var w = 6
if(c < CHAR_WIDTHS.length) {
w = CHAR_WIDTHS[c];
}
var j = 0;
var newChar = 0;
do {
var t = randomSeed += 0x6D2B79F5;
t = Math.imul(t ^ t >>> 15, t | 1);
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
newChar = (((t ^ t >>> 14) >>> 0) / 429496) & 255;
}while((newChar == c || CHAR_WIDTHS[newChar] != w) && ++j < 1000);
c = newChar;
}
if(shadow) {
var ccc = this.makeColor(cr * CHAR_SHADOW_BRIGHTNESS,
cg * CHAR_SHADOW_BRIGHTNESS, cb * CHAR_SHADOW_BRIGHTNESS);
var ox = x;
var ow = 0;
if(bold) this.drawColoredChar(x + 1, y + 1, c, ccc, italic);
x += (ow = this.drawColoredChar(x, y, c, ccc, italic));
if(bold) {
++x;
++ow;
}
if(strikethrough) this.drawCharLine(ox, y + 4, ow, ccc);
if(underline) this.drawCharLine(ox, y + 8, ow, ccc);
}else {
if(cr >= 250 && cg >= 250 && cb >= 250) {
var ox = x;
var ow = 0;
if(bold) this.drawChar(x + 0.5, y + 0.5, c, italic);
x += (ow = this.drawChar(x, y, c, italic));
if(bold) {
++x;
++ow;
}
if(strikethrough) this.drawCharLine(ox, y + 4, ow, "#FFFFFF");
if(underline) this.drawCharLine(ox, y + 8, ow, "#FFFFFF");
}else {
var ccc = this.makeColor(cr,cg,cb);
var ox = x;
var ow = 0;
if(bold) this.drawColoredChar(x + 0.5, y + 0.5, c, ccc, italic);
x += (ow = this.drawColoredChar(x, y, c, ccc, italic));
if(bold) {
++x;
++ow;
}
if(strikethrough) this.drawCharLine(ox, y + 4, ow, ccc);
if(underline) this.drawCharLine(ox, y + 8, ow, ccc);
}
}
if(x > 250.0) {
break;
}
}
if(shadow) {
this.drawString(startX, startY, txt, r, g, b, false, randomSeed, escapeCharChar);
}
}
redraw() {
if(this.dirty || (this.mouseOver && ++this.redrawTimer % 6 == 0) || this.canvas.width != this.containerTag.clientWidth) {
this.dirty = false;
if(this.canvas.width != this.containerTag.clientWidth) {
this.containerTag.style.height = Math.floor(this.containerTag.clientWidth * SERVER_ASPECT_RATIO) + "px";
this.canvas.width = this.containerTag.clientWidth;
this.canvas.height = this.containerTag.clientHeight;
}
var tooltipNewArray = [];
this.ctx.imageSmoothingEnabled = false;
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.fillStyle = "#101010";
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.strokeStyle = "#808080";
this.ctx.lineWidth = this.getPixelX(1.0);
this.ctx.strokeRect(this.ctx.lineWidth / 2, this.ctx.lineWidth / 2,this.canvas.width - this.ctx.lineWidth,this.canvas.height - this.ctx.lineWidth);
if(!this.iconCanvas) {
this.drawIcon(128, 0, 64, 64, 4, 4, 30, 30);
}else {
this.ctx.drawImage(this.iconCanvas, 0, 0, 64, 64, this.getPixelX(4),
this.getPixelY(4), this.getPixelX(30), this.getPixelY(30));
}
var v = Date.now();
var drawAddress = false;
this.drawString(38, 4, this.currentStatus.serverName, 255, 255, 255, true, v);
if(this.currentStatus.pingToServer >= 0) {
if(this.currentStatus.motdLine1.length > 0) {
this.drawString(38, 15, ""+this.currentStatus.motdLine1, 255, 255, 255, true, v);
}
if(this.currentStatus.motdLine2.length > 0) {
this.drawString(38, 27, ""+this.currentStatus.motdLine2, 255, 255, 255, true, v);
}else {
drawAddress = true;
}
}else if(v - this.connecting < 7000) {
this.dirty = true;
var dots = Math.floor(v / 300) % 4;
this.drawString(38, 15, "Connecting" + (dots > 0 ? "." : "") + (dots > 1 ? "." : "") + (dots > 2 ? "." : ""), 128, 128, 128, true, v);
drawAddress = true;
}else {
this.drawString(38, 15, "No Connection", 96, 96, 96, true, v);
drawAddress = true;
}
if(drawAddress && this.showAddress) {
this.drawString(38, 27, this.originalAddress, 48, 48, 48, true, v);
}
var mouseIsOverPing = this.mouseOver && this.mouseX >= 233 && this.mouseX <= 256 && this.mouseY >= 1 && this.mouseY <= 11;
var mouseIsOverPlayers = false;
if(this.currentStatus.pingToServer >= 0) {
if(mouseIsOverPing) tooltipNewArray = [""+this.currentStatus.pingToServer+"ms"];
var i = 5;
if(this.currentStatus.pingToServer < 150) i = 0;
else if(this.currentStatus.pingToServer < 300) i = 1;
else if(this.currentStatus.pingToServer < 600) i = 2;
else if(this.currentStatus.pingToServer < 1000) i = 3;
else i = 4;
this.drawIcon(128, 80 + i * 8, 10, 7, 243, 3);
var onlineStr = "" + this.currentStatus.onlineCount + "/" + this.currentStatus.maxCount;
var xx = 253 - this.getStringWidth(onlineStr);
mouseIsOverPlayers = !mouseIsOverPing && this.mouseOver && this.mouseX >= xx - 5 && this.mouseX <= 256
&& this.mouseY >= 12 && this.mouseY <= 23;
this.drawString(xx, 14, onlineStr, 128, 128, 128, true);
if(mouseIsOverPlayers) {
tooltipNewArray = this.currentStatus.players;
}
}else {
if(v - this.connecting < 7000 || this.connected) {
this.dirty = true;
if(mouseIsOverPing) tooltipNewArray = ["Connecting..."];
this.drawIcon(138, 80 + Math.abs(Math.floor(v / 100.0) % 8 - 4) * 8, 10, 7, 243, 3);
}else {
if(mouseIsOverPing) tooltipNewArray = ["No Connection"];
this.drawIcon(128, 120, 10, 7, 243, 3);
}
}
if(this.showCracked && this.currentStatus.pingToServer >= 0) {
if(this.currentStatus.serverCracked) {
this.drawIcon(144, 64, 16, 16, 243, 25, 10, 10);
}else {
this.drawIcon(176, 96, 16, 16, 243, 25, 11, 11);
}
if(!mouseIsOverPlayers && this.mouseOver && this.mouseX >= 239 && this.mouseX <= 256
&& this.mouseY >= 22 && this.mouseY <= 36) {
if(this.currentStatus.serverCracked) {
tooltipNewArray = ["Server is cracked"];
}else {
tooltipNewArray = ["Server has login"];
}
}
}
if(!tooltipNewArray || !this.mouseOver || tooltipNewArray.length == 0) {
this.tooltipCanvas.style.display = "none";
}else {
this.showTooltip(tooltipNewArray);
}
}
}
ping(addr, name, forceName, hideAddress, hideCracked) {
if(!name) {
name = "Minecraft Server";
this.showQueryName = (typeof forceName === "undefined") || !forceName;
}else {
this.showQueryName = (typeof forceName !== "undefined") && !forceName;
}
this.defaultName = name;
this.showAddress = !hideAddress;
this.originalAddress = addr;
if(typeof hideCracked !== "undefined") {
this.showCracked = !hideCracked;
}
if(addr.indexOf("ws://") != 0 && addr.indexOf("wss://") != 0) {
addr = (window.location.href.indexOf("https:") == 0 ? "wss" : "ws") + "://" + addr;
}
if(this.socket != null && this.connected) {
this.socket.close();
}
this.currentStatus = {
serverName: name,
serverCracked: false,
pingSentTime: Date.now(),
pingToServer: -1,
motdLine1: "Connecting...",
motdLine2: "",
onlineCount: 0,
maxCount: 0,
players: []
};
this.dirty = true;
if(this.connected || this.connecting > 0) {
this.socket.close();
this.connected = false;
this.connecting = 0;
this.redraw();
}
this.connecting = Date.now();
const self = this;
this.socket = new WebSocket(addr);
this.socket.binaryType = "arraybuffer";
this.socket.onopen = () => {
self.socket.send("Accept: MOTD");
self.connecting = 0;
self.connected = true;
self.dirty = true;
};
this.socket.onmessage = (e) => {
if(e.data) {
if(typeof e.data === "string") {
try {
var dat = JSON.parse(e.data);
if(dat["type"].toLowerCase() === "motd") {
self.dirty = true;
if(self.currentStatus.pingSentTime > 0) {
self.currentStatus.pingToServer = Date.now() - self.currentStatus.pingSentTime;
self.currentStatus.pingSentTime = 0;
}
if(this.showQueryName) {
self.currentStatus.serverName = dat["name"];
}else {
self.currentStatus.serverName = this.defaultName;
}
self.currentStatus.serverCracked = dat["cracked"];
var lns = dat["data"]["motd"];
if(lns && lns.length > 0) {
self.currentStatus.motdLine1 = lns[0];
if(lns.length > 1) {
self.currentStatus.motdLine2 = lns[1];
}
}
self.currentStatus.onlineCount = dat["data"]["online"];
self.currentStatus.maxCount = dat["data"]["max"];
self.currentStatus.players = dat["data"]["players"]||[];
}
}catch(e) {
self.currentStatus.motdLine1 = "ERROR: " + e;
}
}else {
var dat = new Uint8Array(e.data);
if(dat.length == 64*64*4) {
this.updateIconFromBytes(dat);
this.dirty = true;
}
}
}
};
this.socket.onclose = (e) => {
self.connecting = 0;
self.connected = false;
self.dirty = true;
};
this.socket.onerror = (e) => {
if(self.connected || self.connecting > 0) {
self.socket.close();
}
self.connecting = 0;
self.connected = false;
self.dirty = true;
};
}
isActive() {
return self.connecting > 0 || self.connected;
}
setTooltip(ttList) {
if(this.isTooltipEnabled) {
var tst = ttList.join();
if(tst !== this.tooltip || this.lastWidth !== this.canvas.width) {
var listWidth = 0;
var listHeight = ttList.length * 9;
for(var i = 0; i < ttList.length; ++i) {
listWidth = Math.max(listWidth, this.getStringWidth(ttList[i]));
}
listWidth += 6;
listHeight += 3;
this.tooltipCanvas.width = this.getPixelX(listWidth);
this.tooltipCanvas.height = this.getPixelY(listHeight);
var mainCtx = this.ctx;
this.ctx = this.tooltipCtx;
this.ctx.imageSmoothingEnabled = false;
this.ctx.clearRect(0, 0, this.tooltipCanvas.width, this.tooltipCanvas.height);
this.ctx.fillStyle = "#101010E0";
this.ctx.fillRect(0, 0, this.tooltipCanvas.width, this.tooltipCanvas.height);
for(var i = 0; i < ttList.length; ++i) {
this.drawString(3, (2 + i*9), ttList[i], 256, 256, 256, true);
}
this.ctx = mainCtx;
this.tooltip = tst;
this.lastWidth = this.canvas.width;
}
}
}
showTooltip(ttList) {
this.dirty = true;
if(!this.mouseOver) {
this.tooltipCanvas.style.display = "none";
return;
}
this.setTooltip(ttList);
this.tooltipCanvas.style.display = "block";
var x = this.mousePageX + this.getPixelX(4);
var y = this.mousePageY - this.getPixelY(9);
if(this.mousePageX + this.tooltipCanvas.width + 2 > window.innerWidth) {
x = window.innerWidth - this.tooltipCanvas.width - 2;
}
if(this.mousePageY + this.tooltipCanvas.height + 2 > window.innerHeight) {
y = window.innerHeight - this.tooltipCanvas.height - 2;
if(y < 0) {
y = 0;
}
}
this.tooltipCanvas.style.left = x + "px";
this.tooltipCanvas.style.top = y + "px";
}
updateIconFromBytes(arr) {
if(!this.iconCanvas) {
this.iconCanvas = document.createElement("canvas");
this.iconCanvas.width = 64;
this.iconCanvas.height = 64;
this.iconContext = this.iconCanvas.getContext("2d");
}
var dat = this.iconContext.createImageData(64, 64);
for(var i = 0, l = 64*64*4; i < l; ++i) {
dat.data[i] = arr[i];
}
this.iconContext.putImageData(dat, 0, 0);
}
}

30
embed.min.js vendored Normal file
View File

@ -0,0 +1,30 @@
// Copyright (c) 2022 Calder Young
var CHAR_WIDTHS=[1,9,9,8,8,8,8,7,9,8,9,9,8,9,9,9,8,8,8,8,9,9,8,9,8,8,8,8,8,9,9,9,4,2,5,6,6,6,6,3,5,5,5,6,2,6,2,6,6,6,6,6,6,6,6,6,6,6,2,2,5,6,5,6,7,6,6,6,6,6,6,6,6,4,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,6,4,6,6,3,6,6,6,6,6,5,6,6,2,6,5,3,6,6,6,6,6,6,6,4,6,6,6,6,6,6,5,2,5,7,6,6,6,6,6,6,6,6,6,6,6,6,4,6,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,6,6,3,6,6,6,6,6,6,6,7,6,6,6,2,6,6,8,9,9,6,6,6,8,8,6,8,8,8,8,8,6,6,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,6,9,9,9,5,9,9,8,7,7,8,7,8,8,8,7,8,8,7,9,9,6,7,7,7,
7,7,9,6,7,8,7,6,6,9,7,6,7,1],CHAR_SHADOW_BRIGHTNESS=.247,SERVER_ASPECT_RATIO=.1484375,ServerEmbed=function(a,c){this.containerTag=a;a.style.width=c;a.style.height=Math.floor(a.clientWidth*SERVER_ASPECT_RATIO)+"px";a.style.backgroundColor="black";this.canvas=document.createElement("canvas");this.canvas.width=a.clientWidth;this.canvas.height=a.clientHeight;a.appendChild(this.canvas);this.ctx=this.canvas.getContext("2d");if(!this.ctx)throw Error("CanvasRenderingContext2D is not supported in this browser");
this.ctx.imageSmoothingEnabled=!1;this.tmpCanvas=document.createElement("canvas");this.tmpCanvas.width=64;this.tmpCanvas.height=64;this.tmpCtx=this.tmpCanvas.getContext("2d");this.tmpCtx.imageSmoothingEnabled=!1;var b=this;this.spriteSheet=document.createElement("img");this.spriteSheet.addEventListener("load",function(){b.isSpriteSheetLoaded=!0;setInterval(function(){return b.redraw()},50)});this.spriteSheet.src="icons.png";this.socket=null;this.dirty=this.connected=!1;this.connecting=0;this.currentStatus=
{serverName:"",serverCracked:!1,pingSentTime:0,pingToServer:-1,motdLine1:"",motdLine2:"",onlineCount:0,maxCount:0,players:[]};this.showCracked=!0;this.tooltip="";this.isShowingTooltip=!1;this.isTooltipEnabled=!0;this.tooltipCanvas=document.createElement("canvas");this.tooltipCanvas.width=64;this.tooltipCanvas.height=64;this.tooltipCanvas.style.display="none";this.tooltipCanvas.style.position="absolute";this.tooltipCanvas.style.zIndex="100";this.tooltipCanvas.style.pointerEvents="none";this.tooltipCtx=
this.tooltipCanvas.getContext("2d");this.tooltipCtx.imageSmoothingEnabled=!1;document.body.appendChild(this.tooltipCanvas);this.mouseOver=!1;this.mousePageY=this.mousePageX=this.mouseY=this.mouseX=0;var g=function(e){b.mouseOver=!0;b.mouseX=e.offsetX/b.canvas.width*256;b.mouseY=e.offsetY/b.canvas.height*38;b.mousePageX=e.pageX;b.mousePageY=e.pageY};this.canvas.addEventListener("mouseover",g);this.canvas.addEventListener("mousemove",g);this.canvas.addEventListener("mouseout",function(e){b.mouseOver=
!1;b.tooltipCanvas.style.display="none"});this.redrawTimer=this.lastWidth=0};ServerEmbed.prototype.setShowCracked=function(a){a!=this.showCracked&&(this.showCracked=a,this.dirty=!0)};ServerEmbed.prototype.setShowTooltip=function(a){this.isTooltipEnabled=a};ServerEmbed.prototype.getPixelX=function(a){return a*this.canvas.width/256};ServerEmbed.prototype.getPixelY=function(a){return a*this.canvas.height/38};
ServerEmbed.prototype.drawIcon=function(a,c,b,g,e,y,f,w){this.isSpriteSheetLoaded&&this.ctx.drawImage(this.spriteSheet,a+.05,c+.05,b-.1,g-.1,this.getPixelX(e),this.getPixelY(y),this.getPixelX(f||b),this.getPixelY(w||g))};
ServerEmbed.prototype.drawChar=function(a,c,b,g){b>=CHAR_WIDTHS.length&&(b=176);g?this.isSpriteSheetLoaded&&(this.ctx.resetTransform(),this.ctx.scale(this.canvas.width/256,this.canvas.height/38),this.ctx.translate(a+1.1,c),this.ctx.transform(1,0,-.35,1,0,0),this.ctx.drawImage(this.spriteSheet,b%16*8,8*Math.floor(b/16),.99*CHAR_WIDTHS[b],7.92,0,0,CHAR_WIDTHS[b],8),this.ctx.resetTransform()):this.drawIcon(b%16*8,8*Math.floor(b/16),CHAR_WIDTHS[b],8,a,c);return CHAR_WIDTHS[b]};
ServerEmbed.prototype.drawColoredChar=function(a,c,b,g,e){b>=CHAR_WIDTHS.length&&(b=176);if(!this.isSpriteSheetLoaded)return CHAR_WIDTHS[b];this.tmpCtx.globalCompositeOperation="source-over";this.tmpCtx.clearRect(0,0,8,8);this.tmpCtx.imageSmoothingEnabled=!1;this.tmpCtx.drawImage(this.spriteSheet,b%16*8,8*Math.floor(b/16),.99*CHAR_WIDTHS[b],7.92,0,0,CHAR_WIDTHS[b],8);this.tmpCtx.globalCompositeOperation="source-in";this.tmpCtx.fillStyle=g;this.tmpCtx.fillRect(0,0,CHAR_WIDTHS[b],8);e?(this.ctx.resetTransform(),
this.ctx.scale(this.canvas.width/256,this.canvas.height/38),this.ctx.translate(a+1.1,c),this.ctx.transform(1,0,-.35,1,0,0),this.ctx.drawImage(this.tmpCanvas,0,0,.99*CHAR_WIDTHS[b],7.92,0,0,CHAR_WIDTHS[b],8),this.ctx.resetTransform()):this.ctx.drawImage(this.tmpCanvas,0,0,.99*CHAR_WIDTHS[b],7.92,this.getPixelX(a),this.getPixelY(c),this.getPixelX(CHAR_WIDTHS[b]),this.getPixelY(8));return CHAR_WIDTHS[b]};
ServerEmbed.prototype.drawCharLine=function(a,c,b,g){this.ctx.fillStyle=g;this.ctx.fillRect(this.getPixelX(a),this.getPixelY(c),this.getPixelX(b+.02),this.getPixelY(1))};ServerEmbed.prototype.makeColor=function(a,c,b){return"rgba("+Math.floor(a)+","+Math.floor(c)+","+Math.floor(b)+",1.0)"};ServerEmbed.prototype.getStringWidth=function(a,c){c=c?c.charCodeAt(0):167;for(var b=0,g=0;g<a.length;++g){var e=a.charCodeAt(g);e==c?g+=2:b=e<CHAR_WIDTHS.length?b+CHAR_WIDTHS[e]:b+6}return b};
ServerEmbed.prototype.drawString=function(a,c,b,g,e,y,f,w,u){var A=u;u=u?u.charCodeAt(0):167;var B=a,D=c;f&&(++a,++c);w||(w=3492589035);for(var h=!1,p=g,q=e,r=y,t=!1,k=!1,m=!1,n=!1,l=!1,C=0;C<b.length;++C){var d=b.charCodeAt(C);if(d==u)h=!0;else if(h)48==d?(p=q=r=0,h=t=k=m=n=l=!1):49==d?(p=q=0,r=170,h=t=k=m=n=l=!1):50==d?(p=r=0,q=170,h=t=k=m=n=l=!1):51==d?(p=0,q=r=170,h=t=k=m=n=l=!1):52==d?(p=170,q=r=0,h=t=k=m=n=l=!1):53==d?(p=r=170,q=0,h=t=k=m=n=l=!1):54==d?(p=255,q=170,r=0,h=t=k=m=n=l=!1):55==d?
(p=q=r=170,h=t=k=m=n=l=!1):56==d?(p=q=r=85,h=t=k=m=n=l=!1):57==d?(p=q=85,r=255,h=t=k=m=n=l=!1):97==d?(p=r=85,q=255,h=t=k=m=n=l=!1):98==d?(p=85,q=r=255,h=t=k=m=n=l=!1):99==d?(q=r=85,p=255,h=t=k=m=n=l=!1):100==d?(p=r=255,q=85,h=t=k=m=n=l=!1):101==d?(p=q=255,r=85,h=t=k=m=n=l=!1):102==d?(p=q=r=255,h=t=k=m=n=l=!1):107==d?(h=!1,t=!0):108==d?(h=!1,k=!0):109==d?(h=!1,m=!0):110==d?(h=!1,n=!0):111==d?(h=!1,l=!0):114==d?(p=g,q=e,r=y,h=t=k=m=n=l=!1):h=!1;else{if(t){this.dirty=!0;var x=6;d<CHAR_WIDTHS.length&&
(x=CHAR_WIDTHS[d]);var z=0;do{var v=w+=1831565813;v=Math.imul(v^v>>>15,v|1);v^=v+Math.imul(v^v>>>7,v|61);v=((v^v>>>14)>>>0)/429496&255}while((v==d||CHAR_WIDTHS[v]!=x)&&1E3>++z);d=v}f?(x=this.makeColor(p*CHAR_SHADOW_BRIGHTNESS,q*CHAR_SHADOW_BRIGHTNESS,r*CHAR_SHADOW_BRIGHTNESS),z=a,k&&this.drawColoredChar(a+1,c+1,d,x,l),a+=d=this.drawColoredChar(a,c,d,x,l),k&&(++a,++d),m&&this.drawCharLine(z,c+4,d,x),n&&this.drawCharLine(z,c+8,d,x)):250<=p&&250<=q&&250<=r?(z=a,k&&this.drawChar(a+.5,c+.5,d,l),a+=d=this.drawChar(a,
c,d,l),k&&(++a,++d),m&&this.drawCharLine(z,c+4,d,"#FFFFFF"),n&&this.drawCharLine(z,c+8,d,"#FFFFFF")):(x=this.makeColor(p,q,r),z=a,k&&this.drawColoredChar(a+.5,c+.5,d,x,l),a+=d=this.drawColoredChar(a,c,d,x,l),k&&(++a,++d),m&&this.drawCharLine(z,c+4,d,x),n&&this.drawCharLine(z,c+8,d,x));if(250<a)break}}f&&this.drawString(B,D,b,g,e,y,!1,w,A)};
ServerEmbed.prototype.redraw=function(){if(this.dirty||this.mouseOver&&0==++this.redrawTimer%6||this.canvas.width!=this.containerTag.clientWidth){this.dirty=!1;this.canvas.width!=this.containerTag.clientWidth&&(this.containerTag.style.height=Math.floor(this.containerTag.clientWidth*SERVER_ASPECT_RATIO)+"px",this.canvas.width=this.containerTag.clientWidth,this.canvas.height=this.containerTag.clientHeight);var a=[];this.ctx.imageSmoothingEnabled=!1;this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);this.ctx.fillStyle=
"#101010";this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height);this.ctx.strokeStyle="#808080";this.ctx.lineWidth=this.getPixelX(1);this.ctx.strokeRect(this.ctx.lineWidth/2,this.ctx.lineWidth/2,this.canvas.width-this.ctx.lineWidth,this.canvas.height-this.ctx.lineWidth);this.iconCanvas?this.ctx.drawImage(this.iconCanvas,0,0,64,64,this.getPixelX(4),this.getPixelY(4),this.getPixelX(30),this.getPixelY(30)):this.drawIcon(128,0,64,64,4,4,30,30);var c=Date.now(),b=!1;this.drawString(38,4,this.currentStatus.serverName,
255,255,255,!0,c);0<=this.currentStatus.pingToServer?(0<this.currentStatus.motdLine1.length&&this.drawString(38,15,""+this.currentStatus.motdLine1,255,255,255,!0,c),0<this.currentStatus.motdLine2.length?this.drawString(38,27,""+this.currentStatus.motdLine2,255,255,255,!0,c):b=!0):(7E3>c-this.connecting?(this.dirty=!0,b=Math.floor(c/300)%4,this.drawString(38,15,"Connecting"+(0<b?".":"")+(1<b?".":"")+(2<b?".":""),128,128,128,!0,c)):this.drawString(38,15,"No Connection",96,96,96,!0,c),b=!0);b&&this.showAddress&&
this.drawString(38,27,this.originalAddress,48,48,48,!0,c);b=this.mouseOver&&233<=this.mouseX&&256>=this.mouseX&&1<=this.mouseY&&11>=this.mouseY;var g=!1;if(0<=this.currentStatus.pingToServer){b&&(a=[""+this.currentStatus.pingToServer+"ms"]);this.drawIcon(128,80+8*(150>this.currentStatus.pingToServer?0:300>this.currentStatus.pingToServer?1:600>this.currentStatus.pingToServer?2:1E3>this.currentStatus.pingToServer?3:4),10,7,243,3);c=""+this.currentStatus.onlineCount+"/"+this.currentStatus.maxCount;var e=
253-this.getStringWidth(c);g=!b&&this.mouseOver&&this.mouseX>=e-5&&256>=this.mouseX&&12<=this.mouseY&&23>=this.mouseY;this.drawString(e,14,c,128,128,128,!0);g&&(a=this.currentStatus.players)}else 7E3>c-this.connecting||this.connected?(this.dirty=!0,b&&(a=["Connecting..."]),this.drawIcon(138,80+8*Math.abs(Math.floor(c/100)%8-4),10,7,243,3)):(b&&(a=["No Connection"]),this.drawIcon(128,120,10,7,243,3));this.showCracked&&0<=this.currentStatus.pingToServer&&(this.currentStatus.serverCracked?this.drawIcon(144,
64,16,16,243,25,10,10):this.drawIcon(176,96,16,16,243,25,11,11),!g&&this.mouseOver&&239<=this.mouseX&&256>=this.mouseX&&22<=this.mouseY&&36>=this.mouseY&&(a=this.currentStatus.serverCracked?["Server is cracked"]:["Server has login"]));a&&this.mouseOver&&0!=a.length?this.showTooltip(a):this.tooltipCanvas.style.display="none"}};
ServerEmbed.prototype.ping=function(a,c,b,g,e){var y=this;c?this.showQueryName="undefined"!==typeof b&&!b:(c="Minecraft Server",this.showQueryName="undefined"===typeof b||!b);this.defaultName=c;this.showAddress=!g;this.originalAddress=a;"undefined"!==typeof e&&(this.showCracked=!e);0!=a.indexOf("ws://")&&0!=a.indexOf("wss://")&&(a=(0==window.location.href.indexOf("https:")?"wss":"ws")+"://"+a);null!=this.socket&&this.connected&&this.socket.close();this.currentStatus={serverName:c,serverCracked:!1,
pingSentTime:Date.now(),pingToServer:-1,motdLine1:"Connecting...",motdLine2:"",onlineCount:0,maxCount:0,players:[]};this.dirty=!0;if(this.connected||0<this.connecting)this.socket.close(),this.connected=!1,this.connecting=0,this.redraw();this.connecting=Date.now();var f=this;this.socket=new WebSocket(a);this.socket.binaryType="arraybuffer";this.socket.onopen=function(){f.socket.send("Accept: MOTD");f.connecting=0;f.connected=!0;f.dirty=!0};this.socket.onmessage=function(w){if(w.data)if("string"===
typeof w.data)try{var u=JSON.parse(w.data);if("motd"===u.type.toLowerCase()){f.dirty=!0;0<f.currentStatus.pingSentTime&&(f.currentStatus.pingToServer=Date.now()-f.currentStatus.pingSentTime,f.currentStatus.pingSentTime=0);f.currentStatus.serverName=y.showQueryName?u.name:y.defaultName;f.currentStatus.serverCracked=u.cracked;var A=u.data.motd;A&&0<A.length&&(f.currentStatus.motdLine1=A[0],1<A.length&&(f.currentStatus.motdLine2=A[1]));f.currentStatus.onlineCount=u.data.online;f.currentStatus.maxCount=
u.data.max;f.currentStatus.players=u.data.players||[]}}catch(B){f.currentStatus.motdLine1="ERROR: "+B}else u=new Uint8Array(w.data),16384==u.length&&(y.updateIconFromBytes(u),y.dirty=!0)};this.socket.onclose=function(w){f.connecting=0;f.connected=!1;f.dirty=!0};this.socket.onerror=function(w){(f.connected||0<f.connecting)&&f.socket.close();f.connecting=0;f.connected=!1;f.dirty=!0}};ServerEmbed.prototype.isActive=function(){return 0<self.connecting||self.connected};
ServerEmbed.prototype.setTooltip=function(a){if(this.isTooltipEnabled){var c=a.join();if(c!==this.tooltip||this.lastWidth!==this.canvas.width){for(var b=0,g=9*a.length,e=0;e<a.length;++e)b=Math.max(b,this.getStringWidth(a[e]));g+=3;this.tooltipCanvas.width=this.getPixelX(b+6);this.tooltipCanvas.height=this.getPixelY(g);b=this.ctx;this.ctx=this.tooltipCtx;this.ctx.imageSmoothingEnabled=!1;this.ctx.clearRect(0,0,this.tooltipCanvas.width,this.tooltipCanvas.height);this.ctx.fillStyle="#101010E0";this.ctx.fillRect(0,
0,this.tooltipCanvas.width,this.tooltipCanvas.height);for(e=0;e<a.length;++e)this.drawString(3,2+9*e,a[e],256,256,256,!0);this.ctx=b;this.tooltip=c;this.lastWidth=this.canvas.width}}};
ServerEmbed.prototype.showTooltip=function(a){this.dirty=!0;if(this.mouseOver){this.setTooltip(a);this.tooltipCanvas.style.display="block";a=this.mousePageX+this.getPixelX(4);var c=this.mousePageY-this.getPixelY(9);this.mousePageX+this.tooltipCanvas.width+2>window.innerWidth&&(a=window.innerWidth-this.tooltipCanvas.width-2);this.mousePageY+this.tooltipCanvas.height+2>window.innerHeight&&(c=window.innerHeight-this.tooltipCanvas.height-2,0>c&&(c=0));this.tooltipCanvas.style.left=a+"px";this.tooltipCanvas.style.top=
c+"px"}else this.tooltipCanvas.style.display="none"};ServerEmbed.prototype.updateIconFromBytes=function(a){this.iconCanvas||(this.iconCanvas=document.createElement("canvas"),this.iconCanvas.width=64,this.iconCanvas.height=64,this.iconContext=this.iconCanvas.getContext("2d"));for(var c=this.iconContext.createImageData(64,64),b=0;16384>b;++b)c.data[b]=a[b];this.iconContext.putImageData(c,0,0)};

BIN
icons.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

BIN
icons_src/background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
icons_src/font.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
icons_src/icons.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
icons_src/key_padlock.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
icons_src/msg_warning.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

14
test.html Normal file
View File

@ -0,0 +1,14 @@
<html>
<head>
<title>Eaglercraft Server Embed Test</title>
<script type="text/javascript" src="embed.js"></script>
<script type="text/javascript">
window.addEventListener("load", () => {
new ServerEmbed(document.getElementById("embed"), "75%").ping("127.0.0.1:25565");
});
</script>
</head>
<body style="background-color:black;">
<div id="embed"></div>
</body>
</html>