Skip to main content

Networking

Networking API allows you to export and import information from your script in real time, currently supporting HTTP (without SSL) and IPv4 TCP connections.

caution

Due to security reasons, Scripter's Rift's networking is implemented from scratch and will bypass the Windows networking stack entirely. It is important to remember this when it comes to the networking API because it means the loopback address 127.0.0.1 will not work and the hosts file is ignored.

To make it possible to ping a server in localhost, SR's network adapter will automatically map localhost into the current computer's IP so that the packet can go to the router and come back to the user's PC, but the listening server has to listen on 0.0.0.0 instead of 127.0.0.1.

TCP

A TCP socket is acquired by calling Network.Connect with the destination after which a polling style API is used to send and receive data. A truely event based approach is not possible as we're constrained by the game loop however it is trivial to wrap it into such an API by binding into Tick or Render events.

-- Translates the hostname if necessary via DNS and returns a socket.
--
Socket Network.Connect(string host, uint port)

-- Writes data to the socket.
-- Returns false on failure (if socket is closed or not yet connected).
--
bool Socket:write(Buffer data) -- Buffer type
bool Socket:write(uint[] data) -- Byte array
bool Socket:write(string data, string? encoding) -- Text

-- Pops the entire buffer, returning null if empty.
--
Buffer? Socket:read()

-- Pops [n] bytes from the buffer, returns null if not available.
--
Buffer? Socket:read(uint n)

-- Closes the socket.
--
void Socket:close()

-- Gets the socket status.
-- 'connecting': Hostname is being resolved and socket is not open yet.
-- 'conneted': Socket is ready for read and write.
-- 'error': An error occured either during connection or DNS.
-- 'fin': Socket was closed by user.
--
string Socket.status
Example/index.ts
// Wait until socket is writable.
//
const sock = Network.Connect('example.org', 80);
do {
await Event.OnTick;
} while (sock.status === 'connecting');

// Write the request, ask the host to close the connection when response is finished.
//
sock.write('GET / HTTP/1.1\r\n');
sock.write('Host: example.org\r\n');
sock.write('Connection: close\r\n');
sock.write('\r\n');

// Dump the output onto console.
//
while (true) {
const data = sock.read();
if (data) {
print(data.toString());
/*
HTTP/1.1 200 OK
Age: 356618
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Sun, 19 Nov 2023 05:22:01 GMT
...
*/
}
await Event.OnTick;
if (sock.status !== 'connected') break;
}

HTTP

For the HTTP interface, SR implements a very similar interface to the fetch API from web standards, two major differences being:

  • The body is not read asynchronously and is immediately available when the promise is resolved.
  • The function never throws and instead sets status to -1 and statusMessage to socket error if the connection fails or the server does not abide by the appropriate HTTP/1.1 standards as defined in RFC-2616.
interface Response {
readonly status: number;
readonly statusMessage: string;
readonly headers: Record<string, string>; // All keys are lower cased.
readonly body: Buffer;
text(): string;
json<T = any>(): T;
}

type HttpMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';

interface FetchOptions {
method?: HttpMethod;
headers?: Record<string, string>;
body?: Buffer | string | { json: any };
}
function fetch(uri: string, options?: FetchOptions): Promise<Response>;
Example/index.ts
const response = await fetch('http://example.org/');
print('status: ', response.status, response.statusMessage);
print('cache control:', response.headers['cache-control']);
print('body length: ', response.body.length);

/*
status: 200 OK
cache control: max-age=604800
body length: 1256
*/

const response = await fetch('http://jsonplaceholder.typicode.com/posts/11', {
method: 'PATCH',
body: { json: { title: 'foo' } }
});
print('status:', response.status, response.statusMessage);
for (const [k, v] of Object.entries(response.json())) {
print(`${k} => ${v}`);
}

/*
status: 200 OK

title => foo
userId => 2
body => delectus reiciendis molestiae occaecati non minima eveniet qui voluptatibus
accusamus in eum beatae sit
vel qui neque voluptates ut commodi qui incidunt
ut animi commodi
id => 11
*/