Capricorn 76
Networking example.
ScriptENGINE includes a network interface for computers 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.

---------------------------
---------------------------
-- 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)