SRL Archive - Research & Development Lounge

Listening/Server Socket plugin


mixster

2009-07-22 03:00:05

Complete edit.

Ok, so updated the plugin and completely rewrote the accompanying RC-ESM.scar. The plugin itself has been altered so that messages (TMessage/TMessArray) have an integer that relates to the client. It can then be used in SendServerData to handle sending stuff. To still allow IP/hostname collection, there is an additional function in the plugin that translates the integer to an IP/Host - GetServerClientHost. On top of this, there is also a function that returns all the new connections that occurred since it was last called - useful if you design something that has a need for greetings. There have probably been a few other plugin related alterations, but on the whole it should be similar.

In regards to the RC-ESM update, I added the option to hash passes through the const hashPass (if you don't want to, then set to False) - uses MD5 hashing, but can be altered to anything really as it is only set in the creation for the server and client so not hard to edit. I then went on simplifying it all down a bit and enclosed some of the similar procedures/functions together so that now it only has a handful of stuff rather than 50. It also allows easy expansion for any other player related things you may want to alter or retrieve with only a couple of TStringArray's needing alteration on the server and an argument & case updated on the client.

Only important thing about RC-ESM is that any custom funcs you add should start at 2 onwards as 0 and 1 are used for Get and Set PlayerData stuffs.

Using RC-ESM is as easy as saving/extracting the attached Scar files into the includes then remove the main loop (with the main loops showing the example scripts) and the meow function in the server file (also part of the example). After that, include the server file in the desired script, have the setup and free'ing of the server and place ReadController somewhere where it should often be run - possibly putting it into a wrapper procedure then assigning it to SRL's procedure that is run when random finding occurs or just putting it into the FindNormalRandoms procedure.
Using the client is as easy as calling the setup, choosing whichever one you need to, then waiting for the server to process it and if needed return a reply. Lastly, make sure you free it, though it won't destroy the world if you don't.

Common problem is the script being stopped with the server still running. If done, then as it runs through the plugin, it doesn't magically free and there is a problem with using the same port - if you switch to another port everything is fine and restarting Scar also fixes.

Anyway, only tested RC-ESM with customly defined functions, but I believe the player stuff *should* work - if not, hit me up on IRC and I'll work on fixing it as soon as I can. After a bug is fixed or the like, I'll paste the alteration done here and the current latest version of each :)

Edit: So there was a small problem with the processing - as I forgot that Scar's connections auto truncate the #13 on the end of messages, it meant that if no additional data was passed, then function integer would be removed and interpret input would through an out of range error due to the blank input being exploded and srl_Explode returning a 0 length array. Anyway, with a small mod to interpret input to stop blank input causing failure and replacing one line in ReadController, it's all good.

ReverseStrArr:
Find
  m := Trunc(l / 2.0);
Change to
  m := Trunc(l / 2.0) - 1;

InterpretInput:
Line after
  Result := '';
Add
  if (input = '') then
begin
Writeln('RC_ESM: Invalid input passed to InterpretInput');
exit;
end;

ReadController:
Find a section of code a dozen or so lines into the function that looks like:
  for i := 0 to Length(read) - 1 do
begin
SetLength(send, 0);
exp := srl_Explode(Copy(read[i].mess, 1, Length(read[i].mess) - 1), #13);
and replace the last line of above section with
    exp := srl_Explode(read[i].mess, #13);


For those who do not want manual edits, here is the latest version for the include file -
Server:
const
hashPass = True;
type
TRC_ESM = record
setup: Boolean;
pass: string;
con: Integer;
custFuncs: array of function(s: string): string;
end;

var
RC_ESM: TRC_ESM;

procedure FreeRCESM;
begin
RC_ESM.setup := False;
try
if (not (FreeServerConnection(RC_ESM.con))) then
Writeln('RC_ESM: Freeing the server connection failed - most likely was not created successfully or already freed');
except
Writeln('RC_ESM: Freeing the server connection threw an exception');
end;
end;

procedure SetCustomFuncs(num: Integer; funcs: function(s: string): string);
begin
if (not (RC_ESM.setup)) then
exit;
if (Length(RC_ESM.custFuncs) <= num) then
SetLength(RC_ESM.custFuncs, num + 1);
RC_ESM.custFuncs[num] := ___Pointer(funcs);
end;

function GetPlayerData(s: string): string;
var
i, t: Integer;
v: TStringArray;
begin
Result := '';
v := ['false', 'names', 'locs'];
for t := 0 to Length(v) - 1 do
if (v[t] = s) then
break;
if (t = Length(v)) then
begin
Writeln('RC-ESM: Invalid option passed to GetPlayerData');
exit;
end;

for i := 0 to Length(Players) - 1 do
begin
case t of
0: if (not (Players[i].Active)) then Result := Result + Players[i].Name + #10;
1: Result := Result + Players[i].Name + #10;
2: Result := Result + Players[i].Loc + #10;
end;
end;
end;

function SetPlayerData(s: string): string;
var
t, p: Integer;
v, e: TStringArray;
begin
Result := '0';

e := srl_Explode(s, #10);
if (Length(e) = 1) then
begin
Writeln('RC-ESM: Invalid string passed to SetPlayerData');
exit;
end;
p := StrToIntDef(e[1], -1);
if ((p = -1) or (p >= Length(Players))) then
begin
Writeln('RC-ESM: Invalid string passed as player number');
exit;
end;

v := ['activate', 'relocate'];
for t := 0 to Length(v) - 1 do
if (v[t] = s) then
break;
if (t = Length(v)) then
begin
Writeln('RC-ESM: Invalid option passed to SetPlayerData');
exit;
end;

case t of
0: Players[p].Active := True;
1: if (Length(e) >= 3) then Players[p].Loc := e[2] else
begin
Writeln('RC-ESM: No location passed to SetPlayerData');
exit;
end;
end;
Result := '1';
end;

function SetupRCESM(pass: string; port: Integer): Boolean;
begin
Result := False;
RC_ESM.setup := True;
if (pass = '') then
RC_ESM.pass := ''
else if (hashPass <> True) then
RC_ESM.pass := pass
else
RC_ESM.pass := MD5(pass);
SetLength(RC_ESM.custFuncs, 0);

try
RC_ESM.con := CreateServerConnection(port);
except
Writeln('RC_ESM: Creating the server connection threw an exception');
exit;
end;

if (RC_ESM.con = -1) then
begin
Writeln('RC_ESM: Unable to create the server connection - you are most likely already using the port for something else');
exit;
end;

SetCustomFuncs(0, @GetPlayerData);
SetCustomFuncs(1, @SetPlayerData);

Result := True;
end;

function RunCustomFunc(ind: Integer; s: string): string;
begin
Result := '0';
if (not (RC_ESM.setup)) then
exit;
if ((ind >= Length(RC_ESM.custFuncs)) or (ind < 0)) then
exit;

Result := RC_ESM.custFuncs[ind](s);
end;

function ImplodeFrom(input: TStringArray; del: string; s, e: Integer): string;
var
i: Integer;
begin
if (s > e) then
Swap(s, e);

if (s < 0) then
s := 0;
if (e >= Length(input)) then
e := Length(input) - 1;

Result := input[s];
for i := s + 1 to e do
Result := Result + del + input[i];
end;

procedure ReverseStrArr(var arr: TStringArray);
var
i, m, l: Integer;
begin
l := Length(arr);
m := Trunc(l / 2.0) - 1;
l := l - 1;

for i := 0 to m do
Swap(arr[i], arr[l - i]);
end;

function InterpretInput(input: string): string;
var
exp: TStringArray;
f: Integer;
s: string;
begin
Result := '';
if (input = '') then
begin
Writeln('RC_ESM: Invalid input passed to InterpretInput');
exit;
end;
exp := srl_Explode(input, #10);
f := StrToIntDef(exp[0], -1);
if ((f = -1) or (f >= Length(RC_ESM.custFuncs))) then
begin
Writeln('RC_ESM: Invalid function number passed to InterpretInput; ' + BoolToStr(f = -1));
exit;
end;

if (Length(exp) > 1) then
s := ImplodeFrom(exp, #10, 2, High(exp))
else
s := '';
Result := RC_ESM.custFuncs[f](s);
end;

procedure Append(var arr: TStringArray; str: string);
var
l: Integer;
begin
l := Length(arr);
SetLength(arr, l + 1);
arr[l] := str;
end;

function ReadController: Boolean;
var
read: TMessArr;
exp, send: TStringArray;
h, i, pl: Integer;
p: Boolean;
s: string;

begin
Result := False;
if (not (RC_ESM.setup)) then
exit;

SetLength(read, 0);
try
read := ReadServerData(RC_ESM.con);
except
Writeln('RC_ESM: Reading the incoming data for the server threw an exception');
exit;
end;

if (Length(read) = 0) then
exit;

p := RC_ESM.pass <> '';
if (p) then
pl := Length(RC_ESM.pass)
else
pl := 0;

for i := 0 to Length(read) - 1 do
begin
SetLength(send, 0);
exp := srl_Explode(read[i].mess, #13);
ReverseStrArr(exp);
h := Length(exp) - 1;
while (h > -1) do
begin
if (p) then
begin
if (Copy(exp[h], 1, pl) = RC_ESM.pass) then
begin
Delete(exp[h], 1, pl + 1);
end
else
begin
try
Writeln('RC_ESM: Host "' + GetServerClientHost(RC_ESM.con, read[i].con) + '" submitted incorrect password');
except
Writeln('RC_ESM: Getting the hostnames/IP''s of the server''s clients threw an exception');
end;
SetLength(exp, Max(h, 0));
h := h - 1;
continue;
end;
end;

s := InterpretInput(exp[h]);
if (s <> '') then
Append(send, s);

SetLength(exp, h);
h := h - 1;
end;

if (Length(send) <> 0) then
try
if (not (SendServerData(RC_ESM.con, read[i].con, srl_Implode(send, #13)))) then
Writeln('RC_ESM: Failed to send server data');
except
Writeln('RC_ESM: Sending server data threw an exception');
end;
end;

Result := True;
end;



Client (added srl_Implode to the top so it doesn't require including srl, which sometimes gave file access errors):
{*******************************************************************************
function srl_Implode(Pieces: TStringArray; Separator: string): string;
By: ZephyrsFury
Description: Combines Pieces in a single string separated by a Separator.
*******************************************************************************}

function srl_Implode(Pieces: TStringArray; Glue: string): string;
var
II, H: Integer;
begin
H := High(Pieces);
if (H < 0) then Exit;
Result := Pieces[0];
if H > 0 then
for II := 1 to H do
Result := Result + Glue + Pieces[II];
end;

const
hashPass = True;

type
TController_ESM = record
connected: Boolean;
pass: string;
con: Integer;
end;

var
Cont_ESM: TController_ESM;

procedure SetupController(host: string; port: Integer; pass: string);
begin
Cont_ESM.con := OpenConnection(host, port, 9000);
if (Cont_ESM.con = -1) then
begin
Writeln('RC_ESM: Unable to connect to the server - most likely it is not running or on a different port');
exit;
end;
if ((hashPass = True) and (pass <> '')) then
Cont_ESM.pass := MD5(pass)
else
Cont_ESM.pass := pass;
Cont_ESM.connected := True;
end;

procedure FreeController;
begin
if (not (Cont_ESM.connected)) then
exit;
Cont_ESM.connected := False;
FreeConnection(Cont_ESM.con);
end;

function FormatCommand(f: Integer; input: string): string;
begin
if (Cont_ESM.pass <> '') then
Result := Cont_ESM.pass + #10
else
Result := '';
Result := Result + IntToStr(f);
if (input <> '') then
Result := Result + #10 + input;
end;

function RequestPlayerData(data: (RC_Inactive, RC_Names, RC_Locs)): string;
var
send: string;
begin
Result := '';
if (not (Cont_ESM.connected)) then
begin
Writeln('RC_ESM: Client not connected in RequestPlayerData');
exit;
end;

case send of
RC_Inactive: send := 'false';
RC_Names: send := 'names';
RC_Locs: send := 'locs';
end;

SendConnectionData(Cont_ESM.con, FormatCommand(0, send));
ReadConnectionData(Cont_ESM.con, Result);
end;

function SubmitPlayerData(data: (RC_Active, RC_Locs); pl: Integer; newVal: string): string;
var
send: string;
begin
Result := '';
if (not (Cont_ESM.connected)) then
begin
Writeln('RC_ESM: Client not connected in SetPlayerData');
exit;
end;

case data of
RC_Active: send := srl_Implode(['activate', IntToStr(pl), newVal], #10);
RC_Locs: send := srl_Implode(['relocate', IntToStr(pl), newVal], #10);
end;

SendConnectionData(Cont_ESM.con, FormatCommand(1, send));
end;

function SubmitCustomFunc(ind: Integer; args: TStringArray; returnData: Boolean): string;
begin
Result := '';
if (not (Cont_ESM.connected)) then
begin
Writeln('RC_ESM: Client not connected in RunCustomFunc');
exit;
end;

SendConnectionData(Cont_ESM.con, FormatCommand(ind, srl_Implode(args, #10)));
if (returnData) then
ReadConnectionData(Cont_ESM.con, Result);
end;

caused

2009-07-22 03:03:33

Great effort ! =).

Brain

2009-07-22 03:18:01

haha you said you like penis

Nava2

2009-07-22 07:43:42

This is nice!

What is the application of this though? Aside from maybe a SCAR BotServ.

mixster

2009-07-22 10:33:08

Anything that requires a listening port - at heart, basically ExternalScarMessaging / Direct client to client messaging over a network/internets.

Stuff like that include IRC Server, remote controlling using a Scar client on a different computer, peer-to-peer instant messaging chat etc.
If you want, I can make an example script that would show an everyday Scar use or one not so everyday Scar use.

n3ss3s

2009-07-22 18:58:38

Cool, this would have helped back then when we didn't have SmartGetFieldArray2D functions, to use interfaces etc with the help of a Java prog :)

mastaraymond

2009-07-22 20:37:43

Wow you sure did a sick job on making such a long post!

It looks like some real epicness, good job boy!

mixster

2009-07-22 21:48:00

If you make a long enough post, then it makes even the dullest of posts seem impressive :)

Boreas

2009-07-23 00:30:40

From: mixster
Anything that requires a listening port - at heart, basically ExternalScarMessaging / Direct client to client messaging over a network/internets.

Stuff like that include IRC Server, remote controlling using a Scar client on a different computer, peer-to-peer instant messaging chat etc.
If you want, I can make an example script that would show an everyday Scar use or one not so everyday Scar use.


RC-ESM.scar would be epic. I need to take back my record on tarajunkys chicken killer:cool:.

mixster

2009-07-23 01:46:03

I suppose I could start setting something up like that - any specific requests for functionality? Of course the basic set user actives etc and an array of procedures for custom launching and stuffs :)

Boreas

2009-07-23 02:49:32

Which players are false, full names, and Loc too. Ability to set individual players to true (not just all). That plus array of custom procedures and I'm a happy camper.

HyperSecret

2009-07-23 03:55:28

From: Boreas
RC-ESM.scar would be epic. I need to take back my record on tarajunkys chicken killer:cool:.


That won't happen :)

mixster

2009-07-24 01:12:53

Updated first post with initial version of RC_ESM.scar - have fun with it, though not sure it will work at all as I can only vouch for the customly defined functions working :)

Boreas

2009-07-28 22:22:29

From: HyperSecret
That won't happen :)


We will see :p

From: mixster
Updated first post with initial version of RC_ESM.scar - have fun with it, though not sure it will work at all as I can only vouch for the customly defined functions working :)


Works with RequestPlayerNames too :D Great work, thanks mixster!

Smartzkid

2009-08-02 09:30:09

Beautiful!

mixster

2009-08-05 03:10:15

Updated and hopefully sexified, read first post for intimate details!