---------------------------
---------------------------
-- Networking example
-- Copyright (c) 2007-09 Capricorn 76 Pty. Ltd.
--
-- ScriptENGINE includes network extensions for computer communications.
-- You can use either the UDP or TCP protocols to transmit data.
-- Note that each protocol has its benefits over the other, and
-- it depends upon your application requirements to decide which to use.
--
-- NOTE:
-- The ./Demos/runDemo.e76script is a utility script used to run all the examples.
-- It loads the world, calls the OnWorldLoad(), OnWorldUnload() functions, and runs the graphics engine loop, waiting for the user to press escape.
---------------------------
---------------------------
-- Load modules used by this demo
require('socket.core');
local listenerId = nil;
local serverId = nil;
local clientId = nil;
local errMsg;
local subGui;
---------------------------
-- PRIVATE function accessible only to this script
---------------------------
local function destroyAllConnections()
if (clientId) then
clientId:close();
clientId = nil;
end
if (listenerId) then
listenerId:close();
listenerId = nil;
end
if (serverId) then
serverId:close();
serverId = nil;
end
end
------------------------
-- PRIVATE Utility function we have written to output a text message to the gui.
--
-- We will write the text message to
-- the listbox depending upon the connection identifier.
--
-- We also make Agent look at the
-- listbox that received the message.
-- This is done by placing two waypoints
-- in the world, and making Agent look at them.
------------------------
local function output(connectionName, txt)
-- We want Agent to look at the listbox that's just received some text.
-- Done by placing waypoints in edit mode & then using them to rotate Agent.
local listId;
if (connectionName == 'client') then
listId = 'listOutputClient';
rotations = IMisc:getLookAtRotations(IWorld:getEntityPosition(idWaypoint_clientPosition), IWorld:getEntityPosition(idAgent_Mech));
else
listId = 'listOutputServer';
rotations = IMisc:getLookAtRotations(IWorld:getEntityPosition(idWaypoint_serverPosition), IWorld:getEntityPosition(idAgent_Mech));
end
------------------------
-- Now make Agent look at the relevant waypoint
-- This model needs a rotation offset applied because of the way it was created.
------------------------
rotations = IMisc:rotationAdd(rotations, '0 -90 0');
IWorld:setEntityRotation(idAgent_Mech, rotations);
------------------------
-- Write the message to the relevant listbox.
-- Scroll the listbox to ensure that the
-- newly added item is visible.
------------------------
idx = IGraphics:addListboxItem(listId, txt);
IGraphics:makeListboxItemVisible(listId, idx);
end
---------------------------
-- runDemo.e76script calls this function when the world is loaded
---------------------------
function OnWorldLoad(worldName)
--print('OnWorldLoad');
---------------------------
-- The SDK world editor creates a worldIds.e76script when the world is saved. It contains all the entity ids.
-- We load the IDs so that we can refer to the world entities using friendly names.
---------------------------
IApp:loadScript(IWorld:getNameLong() .. '/worldIds');
IWorld:setActiveCameraId(idCamera_cam1);
---------------------------
-- Load example's GUI
---------------------------
OnWorldLoadGui();
---------------------------
-- We want to handle 'gui' events.
-- The graphics engine will call our callback function
-- whenever a gui event occurs.
--
-- We can handle the events we're interested in
-- and perform our relevant processing.
--
-- The subscribe function returnes the unique subscriber identifier
-- that we use to unsubscribe later.
---------------------------
subGui = IEvents:subscribeGui('OnGui');
---------------------------
-- Stop the camera auto-processing.
-- ScriptENGINE will no longer move the camera about automatically.
---------------------------
IWorld:setActiveCameraAutoProcessed(false);
end
---------------------------
-- Load this example's GUI
---------------------------
function OnWorldLoadGui()
---------------------------
-- This has all the buttons,
-- listboxes etc. for the user to interact with
-- to control our example.
---------------------------
IGraphics:loadGui('networking');
end
---------------------------
-- runDemo.e76script calls this function when the world is unloaded
---------------------------
function OnWorldUnload(worldName)
--print('OnWorldUnload');
---------------------------
-- Unsubscribe to events.
-- We will no longer receive any events
-- for the specified subscribers.
---------------------------
IEvents:unsubscribe(subGui);
---------------------------
-- Stop all network connections.
-- All active connections will disconnect.
---------------------------
destroyAllConnections();
end
----------------------------
-- Utility function to write some data to the connection.
-- Write the user defined message.
-- Then write some extra data to show
-- the available ScriptENGINE network data types.
----------------------------
local function writeData(netConnectionId, connectionName, editId)
if (netConnectionId) then
-- Write a string to socket, terminated with a CRLF
local bytesWritten, errMsg = netConnectionId:send(tostring(IGraphics:getControlText(editId) .. '\r\n'));
if (bytesWritten) then
output(connectionName, 'Bytes written:' .. bytesWritten);
else
output(connectionName, 'Error on write:' .. errMsg);
end
end
end
----------------------------
-- Utility function to read data from the connection.
-- The data must be read from the connection in the same format as was written.
--
-- When a network event informs us data is available, we call this function to extract the data.
----------------------------
local function readData(netConnectionId, connectionName)
if (netConnectionId) then
----------------------------
-- Is there any data to read?
-- Read a line from the socket
----------------------------
local data, errMsg = netConnectionId:receive('*l');
if (data) then
-- Write the data out to the screen
output(connectionName, data);
else
output(connectionName, 'Error on recv:' .. errMsg);
end
end
end
------------------------
-- Gui event callback function.
-- The graphics engine will call this function
-- whenever a gui event occurs.
-- We check the event type and gui identifier
-- to determine what happened, and act accordingly.
------------------------
function OnGui(callerId, eventType, userData)
---------------------------
-- Start the server/listener.
-- In TCP mode, we create a TCP listener to listen for client connection requests.
---------------------------
if (IGraphics:buttonClicked('buttonStartServer', callerId, eventType)) then
if (not(listenerId)) then
output('listener', '--------------------------');
output('listener', 'Starting listener');
listenerId = assert(socket.tcp(), 'Failed to create TCP listener');
-- Set some parameters on the new socket
listenerId:settimeout(5);
-- Bind the listener to a port and start listening for incoming connections.
-- To process the incoming connections, we must call accept() which creates a new socket to communicate with the client.
local ok, errMsg = listenerId:bind('localhost', 60001);
assert(ok, errMsg);
ok, errMsg = listenerId:listen(1);
assert(ok, errMsg);
output('listener', 'Listener started');
end
return true;
---------------------------
-- Stop the listener and the server
---------------------------
elseif (IGraphics:buttonClicked('buttonStopServer', callerId, eventType)) then
if (listenerId) then
output('listener', '--------------------------');
output('listener', 'Stopping listener');
listenerId:close();
listenerId = nil;
end
if (serverId) then
output('server', '--------------------------');
output('server', 'Stopping server');
serverId:close();
serverId = nil;
end
return true;
---------------------------
-- Start client.
-- In TCP mode, we try to connect to the TCP listener.
---------------------------
elseif (IGraphics:buttonClicked('buttonStartClient', callerId, eventType)) then
if (not(clientId)) then
output('client', '--------------------------');
output('client', 'Starting client...');
-- Create client socket
clientId = assert(socket.tcp(), 'Failed to create TCP client');
-- Set client socket options
--clientId:setoption('reuseaddr', true);
clientId:settimeout(5);
-- Attempt to connect to server
local ok, errMsg = clientId:connect('localhost', 60001);
if (ok) then
local ip, port = clientId:getpeername();
output('client', 'Connected:' .. ip .. ':' .. port);
-- Ask the server to accept the client connection attempt;
serverId, errMsg = listenerId:accept();
if (serverId) then
local ip, port = serverId:getpeername();
output('server', 'Accepted client connection:' .. ip .. ':' .. port);
else
output('server', 'Failed to accept client connection:' .. errMsg);
end
else
output('client', errMsg);
end
end
return true;
---------------------------
-- Stop client
---------------------------
elseif (IGraphics:buttonClicked('buttonStopClient', callerId, eventType)) then
if (clientId) then
output('client', '--------------------------');
output('client', 'Stopping client');
clientId:close();
clientId = nil;
end
return true;
---------------------------
-- Send message from server to client
---------------------------
elseif (IGraphics:buttonClicked('buttonSendServer', callerId, eventType)) then
-- Ask the server to write to socket
writeData(serverId, 'server', 'editServerData');
-- Ask client to read from socket
readData(clientId, 'client');
return true;
---------------------------
-- Send message from client to server
---------------------------
elseif (IGraphics:buttonClicked('buttonSendClient', callerId, eventType)) then
-- Ask client to write to socket
writeData(clientId, 'client', 'editClientData');
-- Ask server to read from socket
readData(serverId, 'server');
return true;
end
end
Copyright © 2006-23 Sep 2009 Capricorn 76 Pty. Ltd. (created on Wed Sep 23 16:49:12 2009)