[Home] [Downloads] [Search] [Help/forum]


Register forum user name Search FAQ

Gammon Forum

[Folder]  Entire forum
-> [Folder]  MUSHclient
. -> [Folder]  Lua
. . -> [Subject]  TCP Socket in Lua and MUSHClient

TCP Socket in Lua and MUSHClient

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page


Posted by Oligo   (26 posts)  [Biography] bio
Date Sat 19 May 2012 12:31 AM (UTC)
Message
I saw an earlier thread below where a user was using Lua to communicate with a C# server utilizing a TCP socket connection.
I'm looking to do something similar. I originally tried to to use VBScript within MUSHClient but it seems the MSWINSOCK.Winsock object no longer exists or ships with Windows 7 so the following failed during object instantiation.
Set winsock = Wscript.CreateObject("MSWINSOCK.Winsock", "winsock_")

So that's made me take a look at Lua since that's where the support is for MUSHClient.

I saw that there's a LuaSocket extension library for Lua. Can I use that library with MUSHClient and if so, how?
Do I need to add some kind of reference or register the LuaSocket dll? I'm unclear how MUSHClient would find or recognize this.
I come from a Wintin.Net/C# background and am completely green to MUSHClient and Lua.
So it's a little frustrating knowing how to run in one language, but learning to crawl in another.

mud.arctic.org : 2700
[Go to top] top

Posted by Oligo   (26 posts)  [Biography] bio
Date Reply #1 on Sat 19 May 2012 04:00 AM (UTC)

Amended on Sat 19 May 2012 04:02 AM (UTC) by Oligo

Message
As a follow up, I got IntelliJ with the Lua plugin installed to facilitate script development.

I got a simple example working in the IDE, but when I copy and paste it to an alias in MUSHClient, I'm getting an error:

[string "Alias: "]:14: unfinished string near '"'


Here's the alias code, all it does it connect to a localhost TCP server and send one text line.

<aliases>
  <alias
   match="testconnect"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>local socket = require("socket")
host = host or "localhost"
port = port or 3000
if arg then
&#9;host = arg[1] or host
&#9;port = arg[2] or port
end
print("Attempting connection to host '" ..host.. "' and port " ..port.. "...")
c = assert(socket.connect(host, port))
print("Connected! Please type stuff (empty line to stop):")
--l = io.read()
l = "this is from MUSHClient test"
while l and l ~= "" and not e do
&#9;assert(c:send(l .. "\n"))
&#9;--l = io.read()
    l = ""
end
</send>
  </alias>
</aliases>



I couldn't figure out why it would work in the IDE but not in MUSH.

mud.arctic.org : 2700
[Go to top] top

Posted by Nick Gammon   Australia  (22,973 posts)  [Biography] bio   Forum Administrator
Date Reply #2 on Sat 19 May 2012 06:23 AM (UTC)
Message
Oligo said:

I saw that there's a LuaSocket extension library for Lua. Can I use that library with MUSHClient and if so, how?


http://www.gammon.com.au/forum/?id=8319

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Oligo   (26 posts)  [Biography] bio
Date Reply #3 on Sat 19 May 2012 01:56 PM (UTC)
Message
Thanks for pointing me to that and it's great to see LuaSocket is native in MUSH. I was able to get a very simple hello world example working messaging from MUSHClient over LuaSocket to a C# TCP server.

Regading the error though I encountered earlier,

[string "Alias: "]:14: unfinished string near '"'


I seem to have isolated it to the newline escape sequence. Specifically the \n. That seems to cause issue with the MUSHClient Lua interpreter. What am I doing wrong or what's the correct way to use the newline escape sequence in a string in MUSH? This works fine in a normal Lua environment.

assert(c:send(l .. "\n"))



mud.arctic.org : 2700
[Go to top] top

Posted by Nick Gammon   Australia  (22,973 posts)  [Biography] bio   Forum Administrator
Date Reply #4 on Sat 19 May 2012 09:25 PM (UTC)
Message
Is this inside a "send" box? That applies another level of handling of backslashes. If you had it in a script file, or a plugin, there would be no problem.

Inside an alias "send" box just do this:


assert(c:send(l .. "\\n"))


MUSHclient will take the \\ and turn it into \ which then gets sent to Lua.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Oligo   (26 posts)  [Biography] bio
Date Reply #5 on Tue 22 May 2012 04:55 AM (UTC)

Amended on Tue 22 May 2012 04:56 AM (UTC) by Oligo

Message
Great. I'm starting to get a little more comfortable with MUSH and Lua, using IntelliJ for dev, and then copy/pasting into the Send box in MUSH. You mentioned a script file, I was using the Send box so the double \ helped fix my issue like you suggested.

However this got me curious about how to reference a script file. I copied and pasted my lua into a file and put it in the MUSHclient\scripts folder.

Also in the UI in the Script: textbox I used the path:

C:\Program Files (x86)\MUSHclient\scripts\healgroupparse.lua

But now I get an error of:

"The script subroutine name must start with a letter and consist of letters, numbers or the underscore character."

I tried healgroupparse.lua and the full path in the Script textbox, neither of which worked.

The script file is Lua that I copy/pasted from the Send box. And I converted the \n back to \n.

mud.arctic.org : 2700
[Go to top] top

Posted by Worstje   Netherlands  (899 posts)  [Biography] bio
Date Reply #6 on Tue 22 May 2012 07:04 AM (UTC)

Amended on Tue 22 May 2012 07:05 AM (UTC) by Worstje

Message
That is because you are putting in the wrong thing; you don't put in the file location there. The Script: box in a trigger/alias/timer refers to a function declared in the global scope of your script (either in your main world, or in your plugin, depending on how you develop/distribute/use your script).

What you want to put in is the name. Suppose you have this function declared: (including comments for teaching purposes)

function HealMeUpScotty(name, line, wildcards, styles)
  -- 'name' holds the name of your trigger/alias/timer, or a
  --     unique id if you did not give it a name.
  -- 'line' holds the plain text that your trigger matched on.
  -- 'wildcards' is an array with all captured groups in your
  --     regular expression.
  -- 'styles' is an array that describes the colours and other
  --     information of your matched text in detail.
  --     (Triggers only.)
  Note("Scotty heals you up!")
end


If you want to use this HealMeUpScotty function, you simply need to write HealMeUpScotty in the Script: box.

You'll either want to use the Lua 'require' or 'include' command to use another file from inside your script space, and for the 'master' scripting file you can input it in the Scripts screen in your world properties.
[Go to top] top

Posted by Oligo   (26 posts)  [Biography] bio
Date Reply #7 on Tue 22 May 2012 01:58 PM (UTC)

Amended on Tue 22 May 2012 02:01 PM (UTC) by Oligo

Message
Thanks for pointing me to where I can point to a script file. In the CTRL+SHIFT+6 dialogue I've defined an external script file, very simple:


function helloWorld(arg1)
    if (tonumber(arg1)==1) then
        Note("hello world!")
    elseif (tonumber(arg1)==2) then
        Note("HELLO WORLD!")
    end
end


But it doesn't seem like that script textbox in the Aliases popup has any impact or do anything. I can reference the function just fine without it? So I'm still a bit confused as to what the Script textbox is for or how to properly use it. To be clear, I'm talkin about the "Script:" textbox between "Label:" and "Group:". The below executes without issue. But if I put helloWorld in the Script textbox and leave the Send empty, nothing happens and if I put helloWorld(2) in the Script textbox I get an error.


<aliases>
  <alias
   match="dohello"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>helloWorld(2)</send>
  </alias>
</aliases>

mud.arctic.org : 2700
[Go to top] top

Posted by Worstje   Netherlands  (899 posts)  [Biography] bio
Date Reply #8 on Tue 22 May 2012 02:34 PM (UTC)
Message
We may just be cross-posting our conversations a bit here, but your example just now is the _first_ method I listed in my other post. I think you understand that.

I'm using an alias for my example now; it is a bit easier to test with, but the principle is the same.

<aliases>
  <alias
   match="!!!"
   enabled="y"
   script="OligoTest"
  >
  </alias>
</aliases>


And use the following function in your script file:

function OligoTest(name, line, wildcards)
  Note("Oligo tests the living daylights out of this alias.")
end
[Go to top] top

Posted by Oligo   (26 posts)  [Biography] bio
Date Reply #9 on Tue 22 May 2012 10:15 PM (UTC)

Amended on Tue 22 May 2012 10:16 PM (UTC) by Oligo

Message
Thanx that example helped a lot and I was able to play around with the 3 arguments. However, it seems the 3 arguments in the method signature seem optional?

The function call seems to work either way, and what you specify regardless of whether you have 1 arg, 2 args, all 3, or no args in the method signature. Also it doesn't seem like what you specify in the Send To: has any bearing if you provide a value in the Script: textbox. So is that the behavior, if a function is provided in the Script: textbox, then anything in the Send To: drop down is ignored?


<aliases>
  <alias
   script="OligoTest"
   match="^blah( [A-Za-z]+)?"
   enabled="y"
   regexp="y"
   send_to="12"
  >
  </alias>
</aliases>





function OligoTest(name, line)--, line, wildcards)
    Note("name: "..name)

    Note("line: "..line)
    --[[
    Note("wildcards: ")
    for i,v in ipairs(wildcards)
    do
        tmp = Trim(v)
        Note(i..": "..tmp)
    end
    --]]
    Note ("in OligoTest")
end



On another note, I will get this thread back on track with respect to TCP and LuaSocket within Mush eventually, I just need to master these basics first..

mud.arctic.org : 2700
[Go to top] top

Posted by Nick Gammon   Australia  (22,973 posts)  [Biography] bio   Forum Administrator
Date Reply #10 on Wed 23 May 2012 01:16 AM (UTC)
Message
Quote:

So is that the behavior, if a function is provided in the Script: textbox, then anything in the Send To: drop down is ignored?


No it shouldn't be. See:

http://www.gammon.com.au/forum/?id=6572

As for the arguments, under the Windows Script Host, you had to get the arguments right (name, line, wildcards).

Lua doesn't really care, so if you don't want the wildcards (or the name or line) you can omit them. But you might be throwing away stuff you need. You might not.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Oligo   (26 posts)  [Biography] bio
Date Reply #11 on Thu 24 May 2012 06:06 PM (UTC)

Amended on Thu 24 May 2012 08:23 PM (UTC) by Oligo

Message
Ok gettin this thread back on subject and why I started it in the first place:

I have a really simple proof of concept lua socket example that I threw in an alias named testConnect. My goal is to establish a TCP socket connection from MUSH client to a simple TCP server I wrote in C# and thus message the MUSH client from the server. After the socket connection is established, I'd like for the socket to perpetually listen for any messages from the server. I've been able to establish this socket connection between MUSH and my custom TCP C# server, but am struggling to progress beyond that. I've encountered 2 issues.

1. if I run it in an alias and use the while loop to receive messages, MUSH basically hangs in that while loop. Is there a way to run an alias or this method in a parallel Lua thread that doesn't impact MUSHclient and its UI.
2. I can't seem to get LuaSocket to receive multiple messages or at least output them until I sever the socket connection by shutting down the TCP server.

I found this thread where another user seemed to encounter a similar issue with respect to the whole blocking issue: http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=11344


<aliases>
  <alias
   script="testConnect"
   match="testconnect"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  </alias>
</aliases>


Below is the Lua in my script file that the alias calls.

function testConnect(name,line,wildscards)
  local socket = require("socket")
  host = "localhost"
  port = 3000
  Note("Attempting connection to host '" ..host.. "' and port " ..port.. "...")

  c = assert(socket.connect(host, port))
  Note("Connected!")
  --l = io.read()
  l = "this is from MUSHClient test";
  assert(c:send(l.."\n"))
  l, e = c:receive()
  while not e do
    Note(l)
    l, e = c:receive()
  end
  Note("end of testConnect()")
end


The below is a simple C# TCP server.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Collections;

namespace ClanServer
{
  public class ArcticChar
  {
    public ArcticChar(NetworkStream pArg)
    {
      socketStream = pArg;
    }
    public int currentMV { get; set; }
    public int maxMV { get; set; }
    public int currentHP { get; set; }
    public int maxHP { get; set; }
    public int charClass { get; set; }
    public int charName { get; set; }
    public NetworkStream socketStream { get; set; }
  }

  //http://www.switchonthecode.com/tutorials/csharp-tutorial-simple-threaded-tcp-server
  class ClanServer2
  {
    private TcpListener tcpListener;
    private Thread listenThread;
    private ArrayList clientList;

    public ClanServer2()
    {
      this.tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 3000);
      this.listenThread = new Thread(new ThreadStart(ListenForClients));
      this.listenThread.Start();
      Console.WriteLine("Awaiting new connections, server started...");
    }

    private void ListenForClients()
    {
      this.tcpListener.Start();
      clientList = new ArrayList();
      while (true)
      {
        //blocks until a client has connected to the server
        TcpClient client = this.tcpListener.AcceptTcpClient();

        //create a thread to handle communication 
        //with connected client
        Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
        Console.WriteLine("New connection received...");
        clientThread.Start(client);
      }
    }

    private void HandleClientComm(object client)
    {
      TcpClient tcpClient = (TcpClient)client;
      NetworkStream clientStream = tcpClient.GetStream();

      clientList.Add(new ArcticChar(clientStream));

      byte[] message = new byte[4096];
      int bytesRead;
      byte[] outboundMsg = new byte[4096];
      outboundMsg = new ASCIIEncoding().GetBytes("here welcome message from server\r\n");
      int bytesSent = outboundMsg.Length;

      while (true)
      {
        bytesRead = 0;

        try
        {
          //blocks until a client sends a message
          bytesRead = clientStream.Read(message, 0, 4096);
          clientStream.Write(outboundMsg, 0, bytesSent);
          clientStream.Flush();
        }
        catch
        {
          //a socket error has occured
          break;
        }

        if (bytesRead == 0)
        {
          Console.WriteLine("the client has disconnected from the server");
          break;
        }

        //message has successfully been received
        ASCIIEncoding encoder = new ASCIIEncoding();
        //System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
        Console.WriteLine(encoder.GetString(message, 0, bytesRead));
      }

      tcpClient.Close();
    }

    public static int Main(String[] args)
    {
      ClanServer2 server = new ClanServer2();
      return 0;
    }
  }

  
}



mud.arctic.org : 2700
[Go to top] top

Posted by Nick Gammon   Australia  (22,973 posts)  [Biography] bio   Forum Administrator
Date Reply #12 on Fri 25 May 2012 01:07 AM (UTC)
Message
I'm a bit doubtful about trying to turn the client into a "second client" for another server.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Oligo   (26 posts)  [Biography] bio
Date Reply #13 on Fri 25 May 2012 03:25 AM (UTC)

Amended on Fri 25 May 2012 03:31 AM (UTC) by Oligo

Message
I've implemented this before in a variety of other clients before about a decade ago. The main goal is to add a level of coordination between clients where you can really implement some advanced AI logic that wouldn't be possible by simply relying on triggers. So in effect, you can have a number of MUSH clients working together as a coordinated team.

The only big difference with respect to MUSH/Lua is the ability to multi-thread and have a socket connection sit in a while loop waiting for incoming messages from the server. But I think your suggestion you made in another thread of checking to read the socket periodically will be a feasible workaround.

mud.arctic.org : 2700
[Go to top] top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


38,450 views.

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]


Written by Nick Gammon - 5K   profile for Nick Gammon on Stack Exchange, a network of free, community-driven Q&A sites   Marriage equality

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( https://gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Hosted at HostDash]