summaryrefslogtreecommitdiffstats
path: root/MCServer/Plugins/NetworkTest
diff options
context:
space:
mode:
Diffstat (limited to 'MCServer/Plugins/NetworkTest')
-rw-r--r--MCServer/Plugins/NetworkTest/Info.lua142
-rw-r--r--MCServer/Plugins/NetworkTest/NetworkTest.lua490
-rw-r--r--MCServer/Plugins/NetworkTest/splashes.txt357
3 files changed, 989 insertions, 0 deletions
diff --git a/MCServer/Plugins/NetworkTest/Info.lua b/MCServer/Plugins/NetworkTest/Info.lua
new file mode 100644
index 000000000..52422d427
--- /dev/null
+++ b/MCServer/Plugins/NetworkTest/Info.lua
@@ -0,0 +1,142 @@
+
+-- Info.lua
+
+-- Implements the g_PluginInfo standard plugin description
+
+g_PluginInfo =
+{
+ Name = "NetworkTest",
+ Version = "1",
+ Date = "2015-01-28",
+ Description = [[Implements test code (and examples) for the cNetwork API]],
+
+ Commands =
+ {
+ },
+
+ ConsoleCommands =
+ {
+ net =
+ {
+ Subcommands =
+ {
+ client =
+ {
+ HelpString = "Connects, as a client, to a specified webpage (google.com by default) and downloads its front page using HTTP",
+ Handler = HandleConsoleNetClient,
+ ParameterCombinations =
+ {
+ {
+ Params = "",
+ Help = "Connects, as a client, to google.com and downloads its front page using HTTP",
+ },
+ {
+ Params = "host [port]",
+ Help = "Connects, as a client, to the specified host and downloads its front page using HTTP",
+ },
+ }, -- ParameterCombinations
+ }, -- client
+
+ close =
+ {
+ HelpString = "Close a listening socket",
+ Handler = HandleConsoleNetClose,
+ ParameterCombinations =
+ {
+ {
+ Params = "[Port]",
+ Help = "Closes a socket listening on the specified port [1024]",
+ },
+ }, -- ParameterCombinations
+ }, -- close
+
+ listen =
+ {
+ HelpString = "Creates a new listening socket on the specified port with the specified service attached to it",
+ Handler = HandleConsoleNetListen,
+ ParameterCombinations =
+ {
+ {
+ Params = "[Port] [Service]",
+ Help = "Starts listening on the specified port [1024] providing the specified service [echo]",
+ },
+ }, -- ParameterCombinations
+ }, -- listen
+
+ lookup =
+ {
+ HelpString = "Looks up the IP addresses corresponding to the given hostname (google.com by default)",
+ Handler = HandleConsoleNetLookup,
+ ParameterCombinations =
+ {
+ {
+ Params = "",
+ Help = "Looks up the IP addresses of google.com.",
+ },
+ {
+ Params = "Hostname",
+ Help = "Looks up the IP addresses of the specified hostname.",
+ },
+ {
+ Params = "IP",
+ Help = "Looks up the canonical name of the specified IP.",
+ },
+ },
+ }, -- lookup
+
+ udp =
+ {
+ Subcommands =
+ {
+ close =
+ {
+ Handler = HandleConsoleNetUdpClose,
+ ParameterCombinations =
+ {
+ {
+ Params = "[Port]",
+ Help = "Closes the UDP endpoint on the specified port [1024].",
+ }
+ },
+ }, -- close
+
+ listen =
+ {
+ Handler = HandleConsoleNetUdpListen,
+ ParameterCombinations =
+ {
+ {
+ Params = "[Port]",
+ Help = "Listens on the specified UDP port [1024], dumping the incoming datagrams into log",
+ },
+ },
+ }, -- listen
+
+ send =
+ {
+ Handler = HandleConsoleNetUdpSend,
+ ParameterCombinations =
+ {
+ {
+ Params = "[Host] [Port] [Message]",
+ Help = "Sends the message [\"hello\"] through UDP to the specified host [localhost] and port [1024]",
+ },
+ },
+ } -- send
+ }, -- Subcommands ("net udp")
+ }, -- udp
+
+ wasc =
+ {
+ HelpString = "Requests the webadmin homepage using https",
+ Handler = HandleConsoleNetWasc,
+ }, -- wasc
+
+ }, -- Subcommands
+ }, -- net
+ },
+}
+
+
+
+
diff --git a/MCServer/Plugins/NetworkTest/NetworkTest.lua b/MCServer/Plugins/NetworkTest/NetworkTest.lua
new file mode 100644
index 000000000..daab0a4bf
--- /dev/null
+++ b/MCServer/Plugins/NetworkTest/NetworkTest.lua
@@ -0,0 +1,490 @@
+
+-- NetworkTest.lua
+
+-- Implements a few tests for the cNetwork API
+
+
+
+
+
+--- Map of all servers currently open
+-- g_Servers[PortNum] = cServerHandle
+local g_Servers = {}
+
+--- Map of all UDP endpoints currently open
+-- g_UDPEndpoints[PortNum] = cUDPEndpoint
+local g_UDPEndpoints = {}
+
+--- List of fortune messages for the fortune server
+-- A random message is chosen for each incoming connection
+-- The contents are loaded from the splashes.txt file on plugin startup
+local g_Fortunes =
+{
+ "Empty splashes.txt",
+}
+
+-- HTTPS certificate to be used for the SSL server:
+local g_HTTPSCert = [[
+-----BEGIN CERTIFICATE-----
+MIIDfzCCAmegAwIBAgIJAOBHN+qOWodcMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV
+BAYTAmN6MQswCQYDVQQIDAJjejEMMAoGA1UEBwwDbG9jMQswCQYDVQQKDAJfWDEL
+MAkGA1UECwwCT1UxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTAxMjQwODQ2MzFa
+Fw0yNTAxMjEwODQ2MzFaMFYxCzAJBgNVBAYTAmN6MQswCQYDVQQIDAJjejEMMAoG
+A1UEBwwDbG9jMQswCQYDVQQKDAJfWDELMAkGA1UECwwCT1UxEjAQBgNVBAMMCWxv
+Y2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJkFYSElu/jw
+nxqjimmj246DejKJK8uy/l9QQibb/Z4kO/3s0gVPOYo0mKv32xUFP7wYIE3XWT61
+zyfvK+1jpnlQTCtM8T5xw/7CULKgLmuIzlQx5Dhy7d+tW46kOjFKwQajS9YzwqWu
+KBOPnFamQWz6vIzuM05+7aIMXbzamInvW/1x3klIrpGQgALwSB1N+oUzTInTBRKK
+21pecUE9t3qrU40Cs5bN0fQBnBjLwbgmnTh6LEplfQZHG5wLvj0IeERVU9vH7luM
+e9/IxuEZluCiu5ViF3jqLPpjYOrkX7JDSKme64CCmNIf0KkrwtFjF104Qylike60
+YD3+kw8Q+DECAwEAAaNQME4wHQYDVR0OBBYEFHHIDTc7mrLDXftjQ5ejU9Udfdyo
+MB8GA1UdIwQYMBaAFHHIDTc7mrLDXftjQ5ejU9UdfdyoMAwGA1UdEwQFMAMBAf8w
+DQYJKoZIhvcNAQEFBQADggEBAHxCJxZPmH9tvx8GKiDV3rgGY++sMItzrW5Uhf0/
+bl3DPbVz51CYF8nXiWvSJJzxhH61hKpZiqvRlpyMuovV415dYQ+Xc2d2IrTX6e+d
+Z4Pmwfb4yaX+kYqIygjXMoyNxOJyhTnCbJzycV3v5tvncBWN9Wqez6ZonWDdFdAm
+J+Moty+atc4afT02sUg1xz+CDr1uMbt62tHwKYCdxXCwT//bOs6W21+mQJ5bEAyA
+YrHQPgX76uo8ed8rPf6y8Qj//lzq/+33EIWqf9pnbklQgIPXJU07h+5L+Y63RF4A
+ComLkzas+qnQLcEN28Dg8QElXop6hfiD0xq3K0ac2bDnjoU=
+-----END CERTIFICATE-----
+]]
+
+local g_HTTPSPrivKey = [[
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZBWEhJbv48J8a
+o4ppo9uOg3oyiSvLsv5fUEIm2/2eJDv97NIFTzmKNJir99sVBT+8GCBN11k+tc8n
+7yvtY6Z5UEwrTPE+ccP+wlCyoC5riM5UMeQ4cu3frVuOpDoxSsEGo0vWM8KlrigT
+j5xWpkFs+ryM7jNOfu2iDF282piJ71v9cd5JSK6RkIAC8EgdTfqFM0yJ0wUSitta
+XnFBPbd6q1ONArOWzdH0AZwYy8G4Jp04eixKZX0GRxucC749CHhEVVPbx+5bjHvf
+yMbhGZbgoruVYhd46iz6Y2Dq5F+yQ0ipnuuAgpjSH9CpK8LRYxddOEMpYpHutGA9
+/pMPEPgxAgMBAAECggEAWxQ4m+I54BJYoSJ2YCqHpGvdb/b1emkvvsumlDqc2mP2
+0U0ENOTS+tATj0gXvotBRFOX5r0nAYx1oO9a1hFaJRsGOz+w19ofLqO6JJfzCU6E
+gNixXmgJ7fjhZiWZ/XzhJ3JK0VQ9px/h+sKf63NJvfQABmJBZ5dlGe8CXEZARNin
+03TnE3RUIEK+jEgwShN2OrGjwK9fjcnXMHwEnKZtCBiYEfD2N+pQmS20gIm13L1t
++ZmObIC24NqllXxl4I821qzBdhmcT7+rGmKR0OT5YKbt6wFA5FPKD9dqlzXzlKck
+r2VAh+JlCtFKxcScmWtQOnVDtf5+mcKFbP4ck724AQKBgQDLk+RDhvE5ykin5R+B
+dehUQZgHb2pPL7N1DAZShfzwSmyZSOPQDFr7c0CMijn6G0Pw9VX6Vrln0crfTQYz
+Hli+zxlmcMAD/WC6VImM1LCUzouNRy37rSCnuPtngZyHdsyzfheGnjORH7HlPjtY
+JCTLaekg0ckQvt//HpRV3DCdaQKBgQDAbLmIOTyGfne74HLswWnY/kCOfFt6eO+E
+lZ724MWmVPWkxq+9rltC2CDx2i8jjdkm90dsgR5OG2EaLnUWldUpkE0zH0ATrZSV
+ezJWD9SsxTm8ksbThD+pJKAVPxDAboejF7kPvpaO2wY+bf0AbO3M24rJ2tccpMv8
+AcfXBICDiQKBgQCSxp81/I3hf7HgszaC7ZLDZMOK4M6CJz847aGFUCtsyAwCfGYb
+8zyJvK/WZDam14+lpA0IQAzPCJg/ZVZJ9uA/OivzCum2NrHNxfOiQRrLPxuokaBa
+q5k2tA02tGE53fJ6mze1DEzbnkFxqeu5gd2xdzvpOLfBxgzT8KU8PlQiuQKBgGn5
+NvCj/QZhDhYFVaW4G1ArLmiKamL3yYluUV7LiW7CaYp29gBzzsTwfKxVqhJdo5NH
+KinCrmr7vy2JGmj22a+LTkjyU/rCZQsyDxXAoDMKZ3LILwH8WocPqa4pzlL8TGzw
+urXGE+rXCwhE0Mp0Mz7YRgZHJKMcy06duG5dh11pAoGBALHbsBIDihgHPyp2eKMP
+K1f42MdKrTBiIXV80hv2OnvWVRCYvnhrqpeRMzCR1pmVbh+QhnwIMAdWq9PAVTTn
+ypusoEsG8Y5fx8xhgjs0D2yMcrmi0L0kCgHIFNoym+4pI+sv6GgxpemfrmaPNcMx
+DXi9JpaquFRJLGJ7jMCDgotL
+-----END PRIVATE KEY-----
+]]
+
+--- Map of all services that can be run as servers
+-- g_Services[ServiceName] = function() -> accept-callbacks
+local g_Services =
+{
+ -- Echo service: each connection echoes back what has been sent to it
+ echo = function (a_Port)
+ return
+ {
+ -- A new connection has come, give it new link-callbacks:
+ OnIncomingConnection = function (a_RemoteIP, a_RemotePort)
+ return
+ {
+ OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
+ LOG("EchoServer(" .. a_Port .. ": Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+ end,
+
+ OnReceivedData = function (a_Link, a_Data)
+ -- Echo the received data back to the link:
+ a_Link:Send(a_Data)
+ end,
+
+ OnRemoteClosed = function (a_Link)
+ end
+ } -- Link callbacks
+ end, -- OnIncomingConnection()
+
+ -- Send a welcome message to newly accepted connections:
+ OnAccepted = function (a_Link)
+ a_Link:Send("Hello, " .. a_Link:GetRemoteIP() .. ", welcome to the echo server @ MCServer-Lua\r\n")
+ end, -- OnAccepted()
+
+ -- There was an error listening on the port:
+ OnError = function (a_ErrorCode, a_ErrorMsg)
+ LOGINFO("EchoServer(" .. a_Port .. ": Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+ end, -- OnError()
+ } -- Listen callbacks
+ end, -- echo
+
+ -- Fortune service: each incoming connection gets a welcome message plus a random fortune text; all communication is ignored afterwards
+ fortune = function (a_Port)
+ return
+ {
+ -- A new connection has come, give it new link-callbacks:
+ OnIncomingConnection = function (a_RemoteIP, a_RemotePort)
+ return
+ {
+ OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
+ LOG("FortuneServer(" .. a_Port .. "): Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+ end,
+
+ OnReceivedData = function (a_Link, a_Data)
+ -- Ignore any received data
+ end,
+
+ OnRemoteClosed = function (a_Link)
+ end
+ } -- Link callbacks
+ end, -- OnIncomingConnection()
+
+ -- Send a welcome message and the fortune to newly accepted connections:
+ OnAccepted = function (a_Link)
+ a_Link:Send("Hello, " .. a_Link:GetRemoteIP() .. ", welcome to the fortune server @ MCServer-Lua\r\n\r\nYour fortune:\r\n")
+ a_Link:Send(g_Fortunes[math.random(#g_Fortunes)] .. "\r\n")
+ end, -- OnAccepted()
+
+ -- There was an error listening on the port:
+ OnError = function (a_ErrorCode, a_ErrorMsg)
+ LOGINFO("FortuneServer(" .. a_Port .. "): Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+ end, -- OnError()
+ } -- Listen callbacks
+ end, -- fortune
+
+ -- HTTPS time - serves current time for each https request received
+ httpstime = function (a_Port)
+ return
+ {
+ -- A new connection has come, give it new link-callbacks:
+ OnIncomingConnection = function (a_RemoteIP, a_RemotePort)
+ local IncomingData = "" -- accumulator for the incoming data, until processed by the http
+ return
+ {
+ OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
+ LOG("https-time server(" .. a_Port .. "): Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+ end,
+
+ OnReceivedData = function (a_Link, a_Data)
+ IncomingData = IncomingData .. a_Data
+ if (IncomingData:find("\r\n\r\n")) then
+ -- We have received the entire request headers, just send the response and shutdown the link:
+ local Content = os.date()
+ a_Link:Send("HTTP/1.0 200 OK\r\nContent-type: text/plain\r\nContent-length: " .. #Content .. "\r\n\r\n" .. Content)
+ a_Link:Shutdown()
+ end
+ end,
+
+ OnRemoteClosed = function (a_Link)
+ LOG("httpstime: link closed by remote")
+ end
+ } -- Link callbacks
+ end, -- OnIncomingConnection()
+
+ -- Start TLS on the new link:
+ OnAccepted = function (a_Link)
+ local res, msg = a_Link:StartTLSServer(g_HTTPSCert, g_HTTPSPrivKey, "")
+ if not(res) then
+ LOG("https-time server(" .. a_Port .. "): Cannot start TLS server: " .. msg)
+ a_Link:Close()
+ end
+ end, -- OnAccepted()
+
+ -- There was an error listening on the port:
+ OnError = function (a_ErrorCode, a_ErrorMsg)
+ LOGINFO("https-time server(" .. a_Port .. "): Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+ end, -- OnError()
+ } -- Listen callbacks
+ end, -- httpstime
+
+ -- TODO: Other services (daytime, ...)
+}
+
+
+
+
+
+function Initialize(a_Plugin)
+ -- Load the splashes.txt file into g_Fortunes, overwriting current content:
+ local idx = 1
+ for line in io.lines(a_Plugin:GetLocalFolder() .. "/splashes.txt") do
+ g_Fortunes[idx] = line
+ idx = idx + 1
+ end
+
+ -- Use the InfoReg shared library to process the Info.lua file:
+ dofile(cPluginManager:GetPluginsPath() .. "/InfoReg.lua")
+ RegisterPluginInfoCommands()
+ RegisterPluginInfoConsoleCommands()
+
+ -- Seed the random generator:
+ math.randomseed(os.time())
+
+ return true
+end
+
+
+
+
+
+function HandleConsoleNetClient(a_Split)
+ -- Get the address to connect to:
+ local Host = a_Split[3] or "google.com"
+ local Port = a_Split[4] or 80
+
+ -- Create the callbacks "personalised" for the address:
+ local Callbacks =
+ {
+ OnConnected = function (a_Link)
+ LOG("Connected to " .. Host .. ":" .. Port .. ".")
+ LOG("Connection stats: Remote address: " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. ", Local address: " .. a_Link:GetLocalIP() .. ":" .. a_Link:GetLocalPort())
+ LOG("Sending HTTP request for front page.")
+ a_Link:Send("GET / HTTP/1.0\r\nHost: " .. Host .. "\r\n\r\n")
+ end,
+
+ OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
+ LOG("Connection to " .. Host .. ":" .. Port .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+ end,
+
+ OnReceivedData = function (a_Link, a_Data)
+ LOG("Received data from " .. Host .. ":" .. Port .. ":\r\n" .. a_Data)
+ end,
+
+ OnRemoteClosed = function (a_Link)
+ LOG("Connection to " .. Host .. ":" .. Port .. " was closed by the remote peer.")
+ end
+ }
+
+ -- Queue a connect request:
+ local res = cNetwork:Connect(Host, Port, Callbacks)
+ if not(res) then
+ LOGWARNING("cNetwork:Connect call failed immediately")
+ return true
+ end
+
+ return true, "Client connection request queued."
+end
+
+
+
+
+
+function HandleConsoleNetClose(a_Split)
+ -- Get the port to close:
+ local Port = tonumber(a_Split[3] or 1024)
+ if not(Port) then
+ return true, "Bad port number: \"" .. a_Split[3] .. "\"."
+ end
+
+ -- Close the server, if there is one:
+ if not(g_Servers[Port]) then
+ return true, "There is no server currently listening on port " .. Port .. "."
+ end
+ g_Servers[Port]:Close()
+ g_Servers[Port] = nil
+ return true, "Port " .. Port .. " closed."
+end
+
+
+
+
+
+function HandleConsoleNetLookup(a_Split)
+ -- Get the name to look up:
+ local Addr = a_Split[3] or "google.com"
+
+ -- Create the callbacks "personalised" for the host:
+ local Callbacks =
+ {
+ OnNameResolved = function (a_Hostname, a_IP)
+ LOG(a_Hostname .. " resolves to " .. a_IP)
+ end,
+
+ OnError = function (a_Query, a_ErrorCode, a_ErrorMsg)
+ LOG("Failed to retrieve information for " .. a_Query .. ": " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+ assert(a_Query == Addr)
+ end,
+
+ OnFinished = function (a_Query)
+ LOG("Resolving " .. a_Query .. " has finished.")
+ assert(a_Query == Addr)
+ end,
+ }
+
+ -- Queue both name and IP DNS queries;
+ -- we don't distinguish between an IP and a hostname in this command so we don't know which one to use:
+ local res = cNetwork:HostnameToIP(Addr, Callbacks)
+ if not(res) then
+ LOGWARNING("cNetwork:HostnameToIP call failed immediately")
+ return true
+ end
+ res = cNetwork:IPToHostname(Addr, Callbacks)
+ if not(res) then
+ LOGWARNING("cNetwork:IPToHostname call failed immediately")
+ return true
+ end
+
+ return true, "DNS query has been queued."
+end
+
+
+
+
+
+function HandleConsoleNetListen(a_Split)
+ -- Get the params:
+ local Port = tonumber(a_Split[3] or 1024)
+ if not(Port) then
+ return true, "Invalid port: \"" .. a_Split[3] .. "\"."
+ end
+ local Service = string.lower(a_Split[4] or "echo")
+
+ -- Create the callbacks specific for the service:
+ if (g_Services[Service] == nil) then
+ return true, "No such service: " .. Service
+ end
+ local Callbacks = g_Services[Service](Port)
+
+ -- Start the server:
+ local srv = cNetwork:Listen(Port, Callbacks)
+ if not(srv:IsListening()) then
+ -- The error message has already been printed in the Callbacks.OnError()
+ return true
+ end
+ g_Servers[Port] = srv
+ return true, Service .. " server started on port " .. Port
+end
+
+
+
+
+
+function HandleConsoleNetUdpClose(a_Split)
+ -- Get the port to close:
+ local Port = tonumber(a_Split[4] or 1024)
+ if not(Port) then
+ return true, "Bad port number: \"" .. a_Split[4] .. "\"."
+ end
+
+ -- Close the server, if there is one:
+ if not(g_UDPEndpoints[Port]) then
+ return true, "There is no UDP endpoint currently listening on port " .. Port .. "."
+ end
+ g_UDPEndpoints[Port]:Close()
+ g_UDPEndpoints[Port] = nil
+ return true, "UDP Port " .. Port .. " closed."
+end
+
+
+
+
+
+function HandleConsoleNetUdpListen(a_Split)
+ -- Get the params:
+ local Port = tonumber(a_Split[4] or 1024)
+ if not(Port) then
+ return true, "Invalid port: \"" .. a_Split[4] .. "\"."
+ end
+
+ local Callbacks =
+ {
+ OnReceivedData = function (a_Endpoint, a_Data, a_RemotePeer, a_RemotePort)
+ LOG("Incoming UDP datagram from " .. a_RemotePeer .. " port " .. a_RemotePort .. ":\r\n" .. a_Data)
+ end,
+
+ OnError = function (a_Endpoint, a_ErrorCode, a_ErrorMsg)
+ LOG("Error in UDP endpoint: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+ end,
+ }
+
+ g_UDPEndpoints[Port] = cNetwork:CreateUDPEndpoint(Port, Callbacks)
+ return true, "UDP listener on port " .. Port .. " started."
+end
+
+
+
+
+
+function HandleConsoleNetUdpSend(a_Split)
+ -- Get the params:
+ local Host = a_Split[4] or "localhost"
+ local Port = tonumber(a_Split[5] or 1024)
+ if not(Port) then
+ return true, "Invalid port: \"" .. a_Split[5] .. "\"."
+ end
+ local Message
+ if (a_Split[6]) then
+ Message = table.concat(a_Split, " ", 6)
+ else
+ Message = "hello"
+ end
+
+ -- Define minimum callbacks for the UDP endpoint:
+ local Callbacks =
+ {
+ OnError = function (a_Endpoint, a_ErrorCode, a_ErrorMsg)
+ LOG("Error in UDP datagram sending: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+ end,
+
+ OnReceivedData = function ()
+ -- ignore
+ end,
+ }
+
+ -- Send the data:
+ local Endpoint = cNetwork:CreateUDPEndpoint(0, Callbacks)
+ Endpoint:EnableBroadcasts()
+ if not(Endpoint:Send(Message, Host, Port)) then
+ Endpoint:Close()
+ return true, "Sending UDP datagram failed"
+ end
+ Endpoint:Close()
+ return true, "UDP datagram sent"
+end
+
+
+
+
+
+function HandleConsoleNetWasc(a_Split)
+ local Callbacks =
+ {
+ OnConnected = function (a_Link)
+ LOG("Connected to webadmin, starting TLS...")
+ local res, msg = a_Link:StartTLSClient("", "", "")
+ if not(res) then
+ LOG("Failed to start TLS client: " .. msg)
+ return
+ end
+ -- We need to send a keep-alive due to #1737
+ a_Link:Send("GET / HTTP/1.0\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n")
+ end,
+
+ OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
+ LOG("Connection to webadmin failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
+ end,
+
+ OnReceivedData = function (a_Link, a_Data)
+ LOG("Received data from webadmin:\r\n" .. a_Data)
+
+ -- Close the link once all the data is received:
+ if (a_Data == "0\r\n\r\n") then -- Poor man's end-of-data detection; works on localhost
+ -- TODO: The Close() method is not yet exported to Lua
+ -- a_Link:Close()
+ end
+ end,
+
+ OnRemoteClosed = function (a_Link)
+ LOG("Connection to webadmin was closed")
+ end,
+ }
+
+ if not(cNetwork:Connect("localhost", "8080", Callbacks)) then
+ LOG("Canot connect to webadmin")
+ end
+
+ return true
+end
+
+
+
+
diff --git a/MCServer/Plugins/NetworkTest/splashes.txt b/MCServer/Plugins/NetworkTest/splashes.txt
new file mode 100644
index 000000000..50a87bd91
--- /dev/null
+++ b/MCServer/Plugins/NetworkTest/splashes.txt
@@ -0,0 +1,357 @@
+As seen on TV!
+Awesome!
+100% pure!
+May contain nuts!
+Better than Prey!
+More polygons!
+Sexy!
+Limited edition!
+Flashing letters!
+Made by Notch!
+It's here!
+Best in class!
+It's finished!
+Kind of dragon free!
+Excitement!
+More than 500 sold!
+One of a kind!
+Heaps of hits on YouTube!
+Indev!
+Spiders everywhere!
+Check it out!
+Holy cow, man!
+It's a game!
+Made in Sweden!
+Uses LWJGL!
+Reticulating splines!
+Minecraft!
+Yaaay!
+Singleplayer!
+Keyboard compatible!
+Undocumented!
+Ingots!
+Exploding creepers!
+That's no moon!
+l33t!
+Create!
+Survive!
+Dungeon!
+Exclusive!
+The bee's knees!
+Down with O.P.P.!
+Closed source!
+Classy!
+Wow!
+Not on steam!
+Oh man!
+Awesome community!
+Pixels!
+Teetsuuuuoooo!
+Kaaneeeedaaaa!
+Now with difficulty!
+Enhanced!
+90% bug free!
+Pretty!
+12 herbs and spices!
+Fat free!
+Absolutely no memes!
+Free dental!
+Ask your doctor!
+Minors welcome!
+Cloud computing!
+Legal in Finland!
+Hard to label!
+Technically good!
+Bringing home the bacon!
+Indie!
+GOTY!
+Ceci n'est pas une title screen!
+Euclidian!
+Now in 3D!
+Inspirational!
+Herregud!
+Complex cellular automata!
+Yes, sir!
+Played by cowboys!
+OpenGL 2.1 (if supported)!
+Thousands of colors!
+Try it!
+Age of Wonders is better!
+Try the mushroom stew!
+Sensational!
+Hot tamale, hot hot tamale!
+Play him off, keyboard cat!
+Guaranteed!
+Macroscopic!
+Bring it on!
+Random splash!
+Call your mother!
+Monster infighting!
+Loved by millions!
+Ultimate edition!
+Freaky!
+You've got a brand new key!
+Water proof!
+Uninflammable!
+Whoa, dude!
+All inclusive!
+Tell your friends!
+NP is not in P!
+Notch <3 ez!
+Music by C418!
+Livestreamed!
+Haunted!
+Polynomial!
+Terrestrial!
+All is full of love!
+Full of stars!
+Scientific!
+Cooler than Spock!
+Collaborate and listen!
+Never dig down!
+Take frequent breaks!
+Not linear!
+Han shot first!
+Nice to meet you!
+Buckets of lava!
+Ride the pig!
+Larger than Earth!
+sqrt(-1) love you!
+Phobos anomaly!
+Punching wood!
+Falling off cliffs!
+0% sugar!
+150% hyperbole!
+Synecdoche!
+Let's danec!
+Seecret Friday update!
+Reference implementation!
+Lewd with two dudes with food!
+Kiss the sky!
+20 GOTO 10!
+Verlet intregration!
+Peter Griffin!
+Do not distribute!
+Cogito ergo sum!
+4815162342 lines of code!
+A skeleton popped out!
+The Work of Notch!
+The sum of its parts!
+BTAF used to be good!
+I miss ADOM!
+umop-apisdn!
+OICU812!
+Bring me Ray Cokes!
+Finger-licking!
+Thematic!
+Pneumatic!
+Sublime!
+Octagonal!
+Une baguette!
+Gargamel plays it!
+Rita is the new top dog!
+SWM forever!
+Representing Edsbyn!
+Matt Damon!
+Supercalifragilisticexpialidocious!
+Consummate V's!
+Cow Tools!
+Double buffered!
+Fan fiction!
+Flaxkikare!
+Jason! Jason! Jason!
+Hotter than the sun!
+Internet enabled!
+Autonomous!
+Engage!
+Fantasy!
+DRR! DRR! DRR!
+Kick it root down!
+Regional resources!
+Woo, facepunch!
+Woo, somethingawful!
+Woo, /v/!
+Woo, tigsource!
+Woo, minecraftforum!
+Woo, worldofminecraft!
+Woo, reddit!
+Woo, 2pp!
+Google anlyticsed!
+Now supports åäö!
+Give us Gordon!
+Tip your waiter!
+Very fun!
+12345 is a bad password!
+Vote for net neutrality!
+Lives in a pineapple under the sea!
+MAP11 has two names!
+Omnipotent!
+Gasp!
+...!
+Bees, bees, bees, bees!
+Jag känner en bot!
+This text is hard to read if you play the game at the default resolution, but at 1080p it's fine!
+Haha, LOL!
+Hampsterdance!
+Switches and ores!
+Menger sponge!
+idspispopd!
+Eple (original edit)!
+So fresh, so clean!
+Slow acting portals!
+Try the Nether!
+Don't look directly at the bugs!
+Oh, ok, Pigmen!
+Finally with ladders!
+Scary!
+Play Minecraft, Watch Topgear, Get Pig!
+Twittered about!
+Jump up, jump up, and get down!
+Joel is neat!
+A riddle, wrapped in a mystery!
+Huge tracts of land!
+Welcome to your Doom!
+Stay a while, stay forever!
+Stay a while and listen!
+Treatment for your rash!
+"Autological" is!
+Information wants to be free!
+"Almost never" is an interesting concept!
+Lots of truthiness!
+The creeper is a spy!
+Turing complete!
+It's groundbreaking!
+Let our battle's begin!
+The sky is the limit!
+Jeb has amazing hair!
+Ryan also has amazing hair!
+Casual gaming!
+Undefeated!
+Kinda like Lemmings!
+Follow the train, CJ!
+Leveraging synergy!
+This message will never appear on the splash screen, isn't that weird?
+DungeonQuest is unfair!
+110813!
+90210!
+Check out the far lands!
+Tyrion would love it!
+Also try VVVVVV!
+Also try Super Meat Boy!
+Also try Terraria!
+Also try Mount And Blade!
+Also try Project Zomboid!
+Also try World of Goo!
+Also try Limbo!
+Also try Pixeljunk Shooter!
+Also try Braid!
+That's super!
+Bread is pain!
+Read more books!
+Khaaaaaaaaan!
+Less addictive than TV Tropes!
+More addictive than lemonade!
+Bigger than a bread box!
+Millions of peaches!
+Fnord!
+This is my true form!
+Totally forgot about Dre!
+Don't bother with the clones!
+Pumpkinhead!
+Hobo humping slobo babe!
+Made by Jeb!
+Has an ending!
+Finally complete!
+Feature packed!
+Boots with the fur!
+Stop, hammertime!
+Testificates!
+Conventional!
+Homeomorphic to a 3-sphere!
+Doesn't avoid double negatives!
+Place ALL the blocks!
+Does barrel rolls!
+Meeting expectations!
+PC gaming since 1873!
+Ghoughpteighbteau tchoghs!
+Déjà vu!
+Déjà vu!
+Got your nose!
+Haley loves Elan!
+Afraid of the big, black bat!
+Doesn't use the U-word!
+Child's play!
+See you next Friday or so!
+From the streets of Södermalm!
+150 bpm for 400000 minutes!
+Technologic!
+Funk soul brother!
+Pumpa kungen!
+日本ハロー!
+한국 안녕하세요!
+Helo Cymru!
+Cześć Polsko!
+你好中国!
+Привет Россия!
+Γεια σου Ελλάδα!
+My life for Aiur!
+Lennart lennart = new Lennart();
+I see your vocabulary has improved!
+Who put it there?
+You can't explain that!
+if not ok then return end
+§1C§2o§3l§4o§5r§6m§7a§8t§9i§ac
+§kFUNKY LOL
+SOPA means LOSER in Swedish!
+Big Pointy Teeth!
+Bekarton guards the gate!
+Mmmph, mmph!
+Don't feed avocados to parrots!
+Swords for everyone!
+Plz reply to my tweet!
+.party()!
+Take her pillow!
+Put that cookie down!
+Pretty scary!
+I have a suggestion.
+Now with extra hugs!
+Now Java 6!
+Woah.
+HURNERJSGER?
+What's up, Doc?
+Now contains 32 random daily cats!
+That's Numberwang!
+pls rt
+Do you want to join my server?
+Put a little fence around it!
+Throw a blanket over it!
+One day, somewhere in the future, my work will be quoted!
+Now with additional stuff!
+Extra things!
+Yay, puppies for everyone!
+So sweet, like a nice bon bon!
+Popping tags!
+Very influential in its circle!
+Now With Multiplayer!
+Rise from your grave!
+Warning! A huge battleship "STEVE" is approaching fast!
+Blue warrior shot the food!
+Run, coward! I hunger!
+Flavor with no seasoning!
+Strange, but not a stranger!
+Tougher than diamonds, rich like cream!
+Getting ready to show!
+Getting ready to know!
+Getting ready to drop!
+Getting ready to shock!
+Getting ready to freak!
+Getting ready to speak!
+It swings, it jives!
+Cruising streets for gold!
+Take an eggbeater and beat it against a skillet!
+Make me a table, a funky table!
+Take the elevator to the mezzanine!
+Stop being reasonable, this is the Internet!
+/give @a hugs 64
+This is good for Realms.
+Any computer is a laptop if you're brave enough! \ No newline at end of file