SRL Archive - OSR Help

Simba sockets


rj

2013-06-30 21:26:43

How do I use the Simba sockets and what are their uses? Can I communicate between simbas with them?



I have no idea what im doing with these:


var
server:integer;
msg:string;

begin
server := CreateSocket;
AcceptSocket(server);
repeat
ListenSocket(server);
until (false);
end.

 var
client:Integer;

begin
Client := CreateSocket;
ConnectSocket(Client, 'localhost','80');
SendSocket(0, 'hello');
end.




I beleive @Brandon was having a field day with sockets but maybe it was someone else

Brandon

2013-07-01 14:48:05

Indeed I was until Simba spoiled my day.. Sockets in Simba is pure garbage. It sucks in Simba but here goes:

Server:

{$I SRL/SRL.Simba}

type TPacket = record
ID: Integer;
Request: Integer;
Data: String;
end;

Function CreateServerSocket(IP, Port: String): Integer;
begin
Result := CreateSocket;
BindSocket(Result, IP, Port);
ListenSocket(Result);
end;

Procedure TerminateServerSocket(Socket: Integer);
begin
CloseSocket(Socket);
FreeSocket(Socket);
end;

Procedure AcceptServerSocket(Socket: Integer; var Sockets: TIntegerArray);
var
Client, Size: Integer;
begin
try
Client := AcceptSocket(Socket);
if (Client <> 0) then
begin
Size := Length(Sockets);
SetLength(Sockets, Size + 1);
Sockets[Size] := Client;
end;
except
end;
end;

Function ReadPacket(Socket: Integer): TPacket;
begin
Result.ID := StrToIntDef(RecvSocket(Socket), -1);
SendSocket(Socket, ';;;');
Result.Request := StrToIntDef(RecvSocket(Socket), -1);
SendSocket(Socket, ';;;');
Result.Data := RecvSocket(Socket);
SendSocket(Socket, ';;;');
end;

Procedure WritePacket(Socket: Integer; Packet: TPacket);
begin
SendSocket(Socket, ToStr(Packet.ID));
RecvSocket(Socket);
SendSocket(Socket, ToStr(Packet.Request));
RecvSocket(Socket);
SendSocket(Socket, Packet.Data);
RecvSocket(Socket);
end;

Procedure PrintPacket(Packet: TPacket);
begin
writeln('ID: ' + ToStr(Packet.ID));
writeln('Request: ' + ToStr(Packet.Request));
writeln('Data: ' + Packet.Data);
writeln('');
end;

var
Socket, I: Integer;
Packet, Reply: TPacket;
Clients: TIntegerArray;

begin
Socket := CreateServerSocket('127.0.0.1', '27015');
AcceptServerSocket(Socket, Clients);

repeat
For I := 0 To High(Clients) Do
begin
try
if (Clients[I] <> -1) then
begin
writeln('Reading Packet..');
Packet := ReadPacket(Clients[I]);
PrintPacket(Packet);

writeln('Sending Packet..');
Reply.ID := Packet.ID;
Reply.Request := -1;
Reply.Data := 'Sent From Server..';
WritePacket(Clients[I], Reply);
writeln('Packet Sent..');
writeln('');
end;
except
FreeSocket(Clients[I]);
Clients[I] := -1;
TerminateServerSocket(Socket);
TerminateScript;
end;
end;
Sleep(100);
ClearSameIntegers(Clients);
until(False);

TerminateServerSocket(Socket);
end.


Client:

type TPacket = record
ID: Integer;
Request: Integer;
Data: String;
end;

Function ReadPacket(Socket: Integer): TPacket;
begin
Result.ID := StrToIntDef(RecvSocket(Socket), -1);
SendSocket(Socket, ';;;');
Result.Request := StrToIntDef(RecvSocket(Socket), -1);
SendSocket(Socket, ';;;');
Result.Data := RecvSocket(Socket);
SendSocket(Socket, ';;;');
end;

Procedure WritePacket(Socket: Integer; Packet: TPacket);
begin
SendSocket(Socket, ToStr(Packet.ID));
RecvSocket(Socket);
SendSocket(Socket, ToStr(Packet.Request));
RecvSocket(Socket);
SendSocket(Socket, Packet.Data);
RecvSocket(Socket);
end;

Procedure PrintPacket(Packet: TPacket);
begin
writeln('ID: ' + ToStr(Packet.ID));
writeln('Request: ' + ToStr(Packet.Request));
writeln('Data: ' + Packet.Data);
writeln('');
end;


var
Socket: Integer;
Packet: TPacket;

begin
Socket := CreateSocket;
ConnectSocket(Socket, '127.0.0.1', '27015');
Packet.ID := 10;
Packet.Request := 25;
Packet.Data := 'Sent From Client..';

Repeat
try
writeln('Sending Packet..');
WritePacket(Socket, Packet);
writeln('Packet Sent..');

writeln('Reading Packet..');
PrintPacket(ReadPacket(Socket));
writeln('');
except
writeln('Socket Disconnected.');
FreeSocket(Socket);
TerminateScript;
end;
Sleep(100);
Until(False);

CloseSocket(Socket);
FreeSocket(Socket);
end.


Yes it can indeed communicate between Simba's and between Simb and anything else really. You can connect to Smart's socket using Simba and mess shit up lol. You can download pages or files and whatever else. Can communicate to webservers/pages, etc. However, the simba sockets are extremely limited when it comes to server client communicating because the code above is supposed to allow multiple clients to connect to the server. It can't though because Simba isn't multi-threaded and the sockets aren't asynchronous. They are blocking sockets. Every send needs a reply and every reply needs a send to make them non-blocking. Still, that leaves them synchronous and thus one server can only have one client because acceptsocket blocks.


The above works but there's so much more it could do when Simba updates its socket API. For example, reading raw data is a lot harder than in C++. For example, if I wanted to write a structure (aka a Packet Record in pascal) to a socket, I have to stringify the whole thing and read by delimiter. OR write, read, write, read, write, read… and have the server read, write, read, write, read, write. That's the non-blocking pattern.

In C++ and many other languages, you can do: Read(Socket, ByteLength); In Simba it's not byte length. It's string length. And I know strings are made of bytes but Simba's sockets don't read them like that.

For example if I do:

send(sock, data, 4); where data is a byte array containing an integer and 4 is sizeof(int);
I can read 4 bytes and it'll give me a single integer.

Simba simba, you can't do that.. It's:

sendsocket(socket, data, 1); where data is a string holding an integer and 1 is the amount of characters in the string. Thus when reading, you can never tell how much to read. You always have to know that the integer is 1 character in the string. What happens if it is 2-3 characters? Then you need to know this and read 2-3 characters. That's actually stupid because an int is always 4 bytes and you can simply just read 4 bytes and get the whole int.


Meh. I got around that by doing the non-blocking pattern. All that is left is to get Simba asynchronous sockets and some other stuffs and it's good to go. I think Dgby is working on it or whoever it was that told me to hold off/wait. They are busy.

Kevin

2013-07-01 15:01:24

Dgby is indeed working on asynchronous sockets and says he will be done once he's finished with the SSL versions.

Kasi is also working on a separate plugin I believe to capture the packets themselves using sockets which could possibly be extended slightly further for more purposes.

Brandon

2013-07-01 15:05:07

I wrote my own socket plugin for Simba but it just isn't the same as if it were to be built into Simba. And yes Kasi is working on a packet sniffer but I think that's a totally different idea all together than communication. That's specifically reading and modifying an existing connection. I also did mention Dbgy is working on it.

rj

2013-07-01 15:22:49

From: Brandon
I wrote my own socket plugin for Simba but it just isn't the same as if it were to be built into Simba. And yes Kasi is working on a packet sniffer but I think that's a totally different idea all together than communication. That's specifically reading and modifying an existing connection. I also did mention Dbgy is working on it.

ahh well I was going to write myself a mini little chat thing.. well nevermind then I guess it looks to be a little bit above my level :s

Brandon

2013-07-01 17:10:02

From: Officer Barbrady
ahh well I was going to write myself a mini little chat thing.. well nevermind then I guess it looks to be a little bit above my level :s



Nah just mess around with it. It still works just fine. Just you'll have to have a central server and all clients connect to that. However that can't be done without the async/non-blocking AcceptSocket. So what you can do is non-blocking connects.


Have one server that accept sockets. When each client wants to send a message or request a message from the server, they need to connect, send, read, disconnect. Simple. Won't take much resources and it allows multiple clients to connect to the server. No need for the server to then worry about cleaning up each client as each client cleans itself. You also can re-use the same socket rather than freeing it.

A chat would be nice actually. Should be fairly easy to do. All you do is just have one server and keep making more clients. Give each client a unique ID and that's it. To give them an ID, just send them a packet with a different count instead of hardcoding it.




And the code is even easier:

Server:

{$I SRL/SRL.Simba}

type TPacket = record
ID: Integer;
Request: Integer;
Data: String;
end;

Function CreateServerSocket(IP, Port: String): Integer;
begin
Result := CreateSocket;
BindSocket(Result, IP, Port);
ListenSocket(Result);
end;

Procedure TerminateServerSocket(Socket: Integer);
begin
CloseSocket(Socket);
FreeSocket(Socket);
end;

Function ReadPacket(Socket: Integer): TPacket;
begin
Result.ID := StrToIntDef(RecvSocket(Socket), -1);
SendSocket(Socket, ';;;');
Result.Request := StrToIntDef(RecvSocket(Socket), -1);
SendSocket(Socket, ';;;');
Result.Data := RecvSocket(Socket);
SendSocket(Socket, ';;;');
end;

Procedure WritePacket(Socket: Integer; Packet: TPacket);
begin
SendSocket(Socket, ToStr(Packet.ID));
RecvSocket(Socket);
SendSocket(Socket, ToStr(Packet.Request));
RecvSocket(Socket);
SendSocket(Socket, Packet.Data);
RecvSocket(Socket);
end;

Procedure PrintPacket(Packet: TPacket);
begin
writeln('ID: ' + ToStr(Packet.ID));
writeln('Request: ' + ToStr(Packet.Request));
writeln('Data: ' + Packet.Data);
writeln('');
end;

var
Socket: Integer;
Request, Reply: TPacket;
Client: Integer;

begin
Socket := CreateServerSocket('127.0.0.1', '27015');

repeat
try
Client := AcceptSocket(Socket);
Request := ReadPacket(Client);
PrintPacket(Request);

Reply.ID := Request.ID;
Reply.Request := 0;
Reply.Data := 'Server Reply';
WritePacket(Client, Reply);
FreeSocket(Client);
except end;
Sleep(100);
until(False);

TerminateServerSocket(Socket);
end.


ClientOne:

type TPacket = record
ID: Integer;
Request: Integer;
Data: String;
end;

Function ReadPacket(Socket: Integer): TPacket;
begin
Result.ID := StrToIntDef(RecvSocket(Socket), -1);
SendSocket(Socket, ';;;');
Result.Request := StrToIntDef(RecvSocket(Socket), -1);
SendSocket(Socket, ';;;');
Result.Data := RecvSocket(Socket);
SendSocket(Socket, ';;;');
end;

Procedure WritePacket(Socket: Integer; Packet: TPacket);
begin
SendSocket(Socket, ToStr(Packet.ID));
RecvSocket(Socket);
SendSocket(Socket, ToStr(Packet.Request));
RecvSocket(Socket);
SendSocket(Socket, Packet.Data);
RecvSocket(Socket);
end;

Procedure PrintPacket(Packet: TPacket);
begin
writeln('ID: ' + ToStr(Packet.ID));
writeln('Request: ' + ToStr(Packet.Request));
writeln('Data: ' + Packet.Data);
writeln('');
end;


var
Socket: Integer;
Packet: TPacket;

begin
Socket := CreateSocket;
Packet.ID := 10;
Packet.Request := 25;
Packet.Data := 'Client Request';

Repeat
try
ConnectSocket(Socket, '127.0.0.1', '27015');
WritePacket(Socket, Packet);
PrintPacket(ReadPacket(Socket));
CloseSocket(Socket);
except end;
Sleep(100);
Until(False);

CloseSocket(Socket);
FreeSocket(Socket);
end.


ClientTwo:

type TPacket = record
ID: Integer;
Request: Integer;
Data: String;
end;

Function ReadPacket(Socket: Integer): TPacket;
begin
Result.ID := StrToIntDef(RecvSocket(Socket), -1);
SendSocket(Socket, ';;;');
Result.Request := StrToIntDef(RecvSocket(Socket), -1);
SendSocket(Socket, ';;;');
Result.Data := RecvSocket(Socket);
SendSocket(Socket, ';;;');
end;

Procedure WritePacket(Socket: Integer; Packet: TPacket);
begin
SendSocket(Socket, ToStr(Packet.ID));
RecvSocket(Socket);
SendSocket(Socket, ToStr(Packet.Request));
RecvSocket(Socket);
SendSocket(Socket, Packet.Data);
RecvSocket(Socket);
end;

Procedure PrintPacket(Packet: TPacket);
begin
writeln('ID: ' + ToStr(Packet.ID));
writeln('Request: ' + ToStr(Packet.Request));
writeln('Data: ' + Packet.Data);
writeln('');
end;


var
Socket: Integer;
Packet: TPacket;

begin
Socket := CreateSocket;
Packet.ID := 11;
Packet.Request := 36;
Packet.Data := 'Client Request';

Repeat
try
ConnectSocket(Socket, '127.0.0.1', '27015');
WritePacket(Socket, Packet);
PrintPacket(ReadPacket(Socket));
CloseSocket(Socket);
except end;
Sleep(100);
Until(False);

CloseSocket(Socket);
FreeSocket(Socket);
end.

rj

2013-07-01 17:31:38

From: Brandon
Nah just mess around with it. It still works just fine. Just you'll have to have a central server and all clients connect to that. However that can't be done without the async/non-blocking AcceptSocket. So what you can do is non-blocking connects.


Have one server that accept sockets. When each client wants to send a message or request a message from the server, they need to connect, send, read, disconnect. Simple. Won't take much resources and it allows multiple clients to connect to the server. No need for the server to then worry about cleaning up each client as each client cleans itself. You also can re-use the same socket rather than freeing it.

A chat would be nice actually. Should be fairly easy to do.

It could be done via a form right?

Brandon

2013-07-01 17:38:04

From: Officer Barbrady
It could be done via a form right?



You see what I posted above your post? Add that to a form, tweak it a bit. Bam chat client. Can be used to control scripts from far away as well.. Or even through a cellphone.
The only protocol is the packet header which I created and called it TPacket. TCP SockStream sockets because that's the default AFAICT and it can't change. You can create your own idea of how the TPacket record should look. It really is up to you. You can even add an image such that it does: TPacket.Image = BitmapToString.. etc.. Fairly easy stuff. Extremely fun.

rj

2013-07-01 17:41:58

From: Brandon
You see what I posted above your post? Add that to a form, tweak it a bit. Bam chat client. Can be used to control scripts from far away as well.
The only protocol is the packet header which I created and called it TPacket. You can create your own idea of how the TPacket record should look. It really is up to you. You can even add an image such that it does: TPacket.Image = BitmapToString.. etc.. Fairly easy stuff. Extremely fun.


Ok, looks cool, for other peope to connect I have to open ports on my router and computer right? Because if I do I don't think it will work because I have a dynamic IP

Brandon

2013-07-01 17:46:07

From: Officer Barbrady
Ok, looks cool, for other peope to connect I have to open ports on my router and computer right? Because if I do I don't think it will work because I have a dynamic IP


Or you can use No-IP.org and have it re-route the IP to your Dynamic one. That works too. It's up to you how you want it done. When I did it in Cpp, I just used No-IP and it redirected all requests to my IP since it's dynamic.

Obviously takes more time to setup No-IP but it's easy.

rj

2013-07-01 17:48:30

From: Brandon
Or you can use No-IP.org and have it re-route the IP to your Dynamic one. That works too. It's up to you how you want it done. When I did it in Cpp, I just used No-IP and it redirected all requests to my IP since it's dynamic.

Obviously takes more time to setup No-IP but it's easy.


Last time I tried to set it up it didn't work out so well :s

Brandon

2013-07-01 22:39:49

From: Officer Barbrady
Last time I tried to set it up it didn't work out so well :s



Wrote a nice protocol & example:


You can keep this if you like. It's actually a good starting point. I probably won't finish it or work on it.

Server:

{$I SRL/SRL.Simba}

const
SERVER_IP = '127.0.0.1';
SERVER_PORT = '27015';
SERVER_ID = '-1';
CLIENT_DISCONNECT = '-2';
SERVER_DISCONNECT = '-3';

type TPacket = record
ToID: String;
FromID: String;
Data: String;
end;




{------------------------------------------------------------------------------}




var
Socket: Integer;
Clients: TIntegerArray;





Function CreateServerSocket(IP, Port: String): Integer;
begin
Result := CreateSocket;
BindSocket(Result, IP, Port);
ListenSocket(Result);
AddOnTerminate('TerminateServerSocket');
end;

Procedure TerminateServerSocket;
begin
CloseSocket(Socket);
FreeSocket(Socket);
end;

Procedure TerminateClientSockets;
var
I: Integer;
begin
For I := 0 To High(Clients) do
begin
try
CloseSocket(Clients[I]);
except end;

try
FreeSocket(Clients[I]);
except end;
end;
end;

Function ReadPacket(Socket: Integer): TPacket;
begin
Result.ToID := RecvSocket(Socket);
SendSocket(Socket, ';;;');
Result.FromID := RecvSocket(Socket);
SendSocket(Socket, ';;;');
Result.Data := RecvSocket(Socket);
SendSocket(Socket, ';;;');
if (Result.Data = '#') then Result.Data := '';
end;

Procedure WritePacket(Socket: Integer; Packet: TPacket);
begin
SendSocket(Socket, Packet.ToID);
RecvSocket(Socket);
SendSocket(Socket, Packet.FromID);
RecvSocket(Socket);
if (Packet.Data = '') then Packet.Data := '#';
SendSocket(Socket, Packet.Data);
RecvSocket(Socket);
end;

Procedure PrintPacket(Packet: TPacket);
begin
writeln('ToID: ' + Packet.ToID);
writeln('FromID: ' + Packet.FromID);
writeln('Data: ' + Packet.Data);
writeln('');
end;

var
Packet: TPacket;
Client: Integer;
IP, Port: String;

begin
Socket := CreateServerSocket(SERVER_IP, SERVER_PORT);

repeat
try
Client := AcceptSocket(Socket);
Packet := ReadPacket(Client);

if ((Packet.ToID = SERVER_ID) And (Packet.FromID = SERVER_ID)) then
begin
SocketInfo(Client, IP, Port);
Packet.FromID := CompressString(MD5(IP + Port));
end;

Packet.Data := '';
WritePacket(Client, Packet);
FreeSocket(Client);
except
FreeSocket(Client);
end;
Sleep(100);
until(False);
end.


Client:

{$I SRL/SRL.Simba}

const
SERVER_IP = '127.0.0.1';
SERVER_PORT = '27015';
SERVER_ID = '-1';
CLIENT_DISCONNECT = '-2';
SERVER_DISCONNECT = '-3';

type TPacket = record
ToID: String;
FromID: String;
Data: String;
end;




{------------------------------------------------------------------------------}




var
Socket: Integer;
Clients: TIntegerArray;





Function CreateServerSocket(IP, Port: String): Integer;
begin
Result := CreateSocket;
BindSocket(Result, IP, Port);
ListenSocket(Result);
AddOnTerminate('TerminateServerSocket');
end;

Procedure TerminateServerSocket;
begin
CloseSocket(Socket);
FreeSocket(Socket);
end;

Procedure TerminateClientSockets;
var
I: Integer;
begin
For I := 0 To High(Clients) do
begin
try
CloseSocket(Clients[I]);
except end;

try
FreeSocket(Clients[I]);
except end;
end;
end;

Function ReadPacket(Socket: Integer): TPacket;
begin
Result.ToID := RecvSocket(Socket);
SendSocket(Socket, ';;;');
Result.FromID := RecvSocket(Socket);
SendSocket(Socket, ';;;');
Result.Data := RecvSocket(Socket);
SendSocket(Socket, ';;;');
if (Result.Data = '#') then Result.Data := '';
end;

Procedure WritePacket(Socket: Integer; Packet: TPacket);
begin
SendSocket(Socket, Packet.ToID);
RecvSocket(Socket);
SendSocket(Socket, Packet.FromID);
RecvSocket(Socket);
if (Packet.Data = '') then Packet.Data := '#';
SendSocket(Socket, Packet.Data);
RecvSocket(Socket);
end;

Procedure PrintPacket(Packet: TPacket);
begin
writeln('ToID: ' + Packet.ToID);
writeln('FromID: ' + Packet.FromID);
writeln('Data: ' + Packet.Data);
writeln('');
end;









var
Form: TForm;
ClientImage: TImage;
ServerImage: TImage;
HistoryBox: TMemo;
SendBox: TMemo;
ClientBox: TListBox;
SendButton: TButton;
Params: TVariantArray;
Terminate: Boolean;
Packet: TPacket;

Procedure OnFormShow(Sender: TObject);
begin
try
SetLength(Clients, Length(Clients) + 1);
Clients[0] := CreateSocket;
HistoryBox.Text := HistoryBox.Text + 'Connecting To Server..' + #13#10;
ConnectSocket(Clients[0], SERVER_IP, SERVER_PORT);

Packet.FromID := SERVER_ID;
Packet.ToID := SERVER_ID;
WritePacket(Clients[0], Packet);
Packet := ReadPacket(Clients[0]);
CloseSocket(Clients[0]);
HistoryBox.Text := HistoryBox.Text + 'Connection Successful.' + #13#10#13#10;
except
ClearDebug;
Clients[0] := -1;
HistoryBox.Text := HistoryBox.Text + 'Error Connection Failed: Server Unavailable.' + #13#10;
end;
end;

Procedure OnFormClose(Sender: TObject; var Action: TCloseAction);
begin
Terminate := True;
end;

Procedure OnSendClicked(Sender: TObject);
begin
if (Clients[0] <> -1) then
begin
try
if (SendBox.Text <> '') then
begin
Packet.Data := SendBox.Text;
ConnectSocket(Clients[0], SERVER_IP, SERVER_PORT);
WritePacket(Clients[0], Packet);
CloseSocket(Clients[0]);
SendBox.Clear;
HistoryBox.Text := HistoryBox.Text + 'Sent: ' + Packet.Data + #13#10;
end;
except
HistoryBox.Text := HistoryBox.Text + 'Error Sending Message.' + #13#10;
end;
end else
HistoryBox.Text := HistoryBox.Text + 'Server Unavailable.' + #13#10;
end;

Procedure OnSendKeyPressed(Sender: TObject; var Key: Char);
begin
if ((Key = Chr(VK_ENTER)) And (Not IsKeyDown(VK_SHIFT))) then
begin
if (Clients[0] <> -1) then
begin
try
if (SendBox.Text <> '') then
begin
Packet.Data := SendBox.Text;
ConnectSocket(Clients[0], SERVER_IP, SERVER_PORT);
WritePacket(Clients[0], Packet);
CloseSocket(Clients[0]);
SendBox.Clear;
HistoryBox.Text := HistoryBox.Text + 'Sent: ' + Packet.Data + #13#10;
end;
except
HistoryBox.Text := HistoryBox.Text + 'Error Sending Message.' + #13#10;
end;
end else
HistoryBox.Text := HistoryBox.Text + 'Server Unavailable.' + #13#10;
end;
end;

Procedure OnSendKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if ((Key = VK_ENTER) And (SendBox.Text = '')) then
Key := 0;
end;

procedure InitialiseForm;
var
X, Y: Integer;
begin
Form := TForm.Create(nil);
Form.OnShow := @OnFormShow;
Form.OnClose := @OnFormClose;
Form.SetBounds(256, 256, 725, 500);
Form.Caption := 'Client';

X := 10; Y := 25;
ClientImage := TImage.Create(Form);
ClientImage.Parent := Form;
ClientImage.Center := True;
ClientImage.Stretch := True;
ClientImage.Picture.Bitmap.Assign(GetMufasaBitmap(BitmapFromClient(0, 0, 96, 96)).ToTBitmap);
ClientImage.SetBounds(X, Y, 106, 106);
ClientImage.Visible := True;

ServerImage := TImage.Create(Form);
ServerImage.Parent := Form;
ServerImage.Center := True;
ServerImage.Stretch := True;
ServerImage.Picture.Bitmap.Assign(GetMufasaBitmap(BitmapFromClient(0, 0, 96, 96)).ToTBitmap);
ServerImage.SetBounds(X, 256, 106, 106);
ServerImage.Visible := True;

HistoryBox := TMemo.Create(Form);
HistoryBox.Parent := Form;
HistoryBox.SetBounds(X + ClientImage.Width + 15, 25, 450, ServerImage.Top + ServerImage.Height - 25);
HistoryBox.Enabled := False;
HistoryBox.Visible := True;

SendBox := TMemo.Create(Form);
SendBox.Parent := Form;
SendBox.SetBounds(X, ServerImage.Top + ServerImage.Height + 15, HistoryBox.Left + HistoryBox.Width - 10, 110);
SendBox.OnKeyPress := @OnSendKeyPressed;
SendBox.OnKeyDown := @OnSendKeyDown;
SendBox.Visible := True;

ClientBox := TListBox.Create(Form);
ClientBox.Parent := Form;
ClientBox.SetBounds(HistoryBox.Left + HistoryBox.Width + 10, HistoryBox.Top, 125, HistoryBox.Top + HistoryBox.Height - 25);
ClientBox.Visible := True;

SendButton := TButton.Create(Form);
SendButton.Parent := Form;
SendButton.Caption := 'Send Message';
SendButton.SetBounds(SendBox.Left + SendBox.Width + 10, SendBox.Top, ClientBox.Width, SendBox.Height);
SendButton.OnClick := @OnSendClicked;
SendBox.Visible := True;
Form.ShowModal;
end;


begin
ThreadSafeCall('InitialiseForm', Params);
Terminate := False;
Form.Close;
Form.Free;
end.

rj

2013-07-01 22:41:11

Thanks, I may do this later, I'll have to get no-ip working though