Пример #1
0
        /// <summary>
        /// Sends dialog numberrange message, showing a message and a small text field,
        /// for the user to put in a number.
        /// </summary>
        /// <remarks>
        /// NUMBERRANGE uses CZ_DIALOG_SELECT for its response,
        /// which means the number range is that of a byte, 0~255.
        ///
        /// Parameters:
        /// - string message
        /// - int min (optional, defaults to 0)
        /// - int max (optional, defaults to 255)
        ///
        /// Result:
        /// The number put in by the user.
        /// Returns 0 on error.
        /// </remarks>
        /// <param name="L"></param>
        /// <returns></returns>
        private int numinput(IntPtr L)
        {
            // Check arguments and return 0 on error
            var argc = Melua.lua_gettop(L);

            if (argc == 0)
            {
                Log.Warning("numinput: No arguments.");
                Melua.lua_pushinteger(L, 0);
                return(1);
            }

            var conn = this.GetConnectionFromState(L);

            int min = 0, max = 255;

            // Get arguments
            var msg = Melua.luaL_checkstring(L, 1);

            if (argc >= 3)
            {
                min = Melua.luaL_checkinteger(L, 2);
                max = Melua.luaL_checkinteger(L, 3);
            }

            Melua.lua_pop(L, argc);

            this.HandleCustomCode(conn, ref msg);
            this.AttachNpcName(conn, ref msg);

            Send.ZC_DIALOG_NUMBERRANGE(conn, msg, min, max);

            return(Melua.lua_yield(L, 1));
        }
Пример #2
0
        /// <summary>
        /// Sends dialog select message to client, showing a message and a
        /// list of options to select from.
        /// </summary>
        /// <remarks>
        /// Select can take an arbitrary amount of options.
        ///
        /// Parameters:
        /// - string message
        /// - string options...
        ///
        /// Result:
        /// The number of the selected option, starting from 1.
        /// Returns 0 on error.
        /// </remarks>
        /// <param name="L"></param>
        /// <returns></returns>
        private int select(IntPtr L)
        {
            // Check arguments and return 0 on error
            var argc = Melua.lua_gettop(L);

            if (argc == 0)
            {
                Log.Warning("select: No arguments.");
                Melua.lua_pushinteger(L, 0);
                return(1);
            }

            var conn = this.GetConnectionFromState(L);

            // Get arguments, first argument is the message, everything afterwards
            // is one option to select from.
            var args = new string[argc];

            for (int i = 1; i <= argc; ++i)
            {
                var arg = Melua.luaL_checkstring(L, i);
                this.HandleCustomCode(conn, ref arg);
                args[i - 1] = arg;
            }

            Melua.lua_pop(L, argc);

            this.AttachNpcName(conn, ref args[0]);

            Send.ZC_DIALOG_SELECT(conn, args);

            return(Melua.lua_yield(L, 1));
        }
Пример #3
0
        /// <summary>
        /// Sends dialog input message, showing a message and a text field,
        /// for the user to put in a string.
        /// </summary>
        /// <remarks>
        /// Parameters:
        /// - string message
        ///
        /// Result:
        /// The string put in by the user.
        /// Returns empty string on error.
        /// </remarks>
        /// <param name="L"></param>
        /// <returns></returns>
        private int input(IntPtr L)
        {
            // Check arguments and return empty string on error
            var argc = Melua.lua_gettop(L);

            if (argc == 0)
            {
                Log.Warning("input: No arguments.");
                Melua.lua_pushstring(L, "");
                return(1);
            }

            var conn = this.GetConnectionFromState(L);

            // Get message
            var msg = Melua.luaL_checkstring(L, 1);

            Melua.lua_pop(L, 1);

            this.HandleCustomCode(conn, ref msg);
            this.AttachNpcName(conn, ref msg);

            Send.ZC_DIALOG_STRINGINPUT(conn, msg);

            return(Melua.lua_yield(L, 1));
        }
Пример #4
0
        /// <summary>
        /// Calls function with connection's script state.
        /// </summary>
        /// <param name="conn"></param>
        /// <param name="functionName"></param>
        /// <param name="arguments"></param>
        public ScriptCallResult Call(ChannelConnection conn, string functionName, params object[] arguments)
        {
            if (conn.ScriptState.LuaThread != null)
            {
                Log.Warning("ScriptManager.Call: A previous thread wasn't closed for user '{0}'.", conn.Account.Name);
            }

            // Prepare thread
            conn.ScriptState.LuaThread = this.GetNewThread(conn.ScriptState);

            var NL  = conn.ScriptState.LuaThread.L;
            var top = Melua.lua_gettop(_gL);

            // Get function
            Melua.lua_getglobal(NL, functionName);
            if (Melua.lua_isnil(NL, -1))
            {
                conn.ScriptState.Reset();
                return(new ScriptCallResult(ScriptCallResultType.NotFound));
            }

            // Push all arguments
            var argc = arguments?.Length ?? 0;

            if (argc != 0)
            {
                PushArguments(NL, arguments);
            }

            // Execute the function with the arguments
            var funcResult = Melua.lua_resume(NL, argc);

            // If result is not a success or yield, an error occurred.
            if (funcResult != 0 && funcResult != Melua.LUA_YIELD)
            {
                conn.ScriptState.Reset();

                var errorMessage = string.Format("Error while executing '{0}' for user '{1}': {2}", functionName, conn.Account.Name, Melua.lua_tostring(NL, -1));
                return(new ScriptCallResult(ScriptCallResultType.Error, errorMessage));
            }

            // We currently don't expect functions called this way to yield
            if (funcResult == Melua.LUA_YIELD)
            {
                Log.Warning("ScriptManager.Call: Script function '{0}' yielded for user '{1}', a behavior that's currently not handled.", functionName, conn.Account.Name);
            }

            conn.ScriptState.Reset();
            return(new ScriptCallResult(ScriptCallResultType.Success));
        }
Пример #5
0
        /// <summary>
        /// Calls function with connection's script state.
        /// </summary>
        /// <param name="conn"></param>
        /// <param name="functionName"></param>
        public void Call(ChannelConnection conn, string functionName)
        {
            if (conn.ScriptState.LuaThread != null)
            {
                Log.Warning("ScriptManager.Call: A previous thread wasn't closed for user '{0}'.", conn.Account.Name);
            }

            // Get function name, use oneliner for localized, single-line
            // dialogues.
            if (this.IsOneLiner(functionName))
            {
                functionName = "npc_oneliner";
            }

            // Prepare thread
            conn.ScriptState.LuaThread = this.GetNewThread(conn.ScriptState);
            var NL  = conn.ScriptState.LuaThread.L;
            var top = Melua.lua_gettop(GL);

            // Get function
            Melua.lua_getglobal(NL, functionName);
            if (Melua.lua_isnil(NL, -1))
            {
                Log.Error("ScriptManager.Call: Function '{0}' not found.", functionName);
                return;
            }

            // Run
            var result = Melua.lua_resume(NL, 0);

            // Log error if result is not success or yield
            if (result != 0 && result != Melua.LUA_YIELD)
            {
                Log.Error("ScriptManager.Call: Error while executing '{0}' for {1}.\n{2}", functionName, conn.Account.Name, Melua.lua_tostring(NL, -1));
                result = 0;                 // Set to 0 to close dialog on error
            }

            // Close dialog if end of function was reached
            if (result == 0)
            {
                Send.ZC_DIALOG_CLOSE(conn);

                conn.ScriptState.Reset();
            }
        }
Пример #6
0
        /// <summary>
        /// Creates new Lua thread for the state to use and saves
        /// a reference to the connection's script state.
        /// </summary>
        /// <param name="conn"></param>
        /// <returns></returns>
        public LuaThread GetNewThread(ScriptState state)
        {
            if (state.LuaThread != null)
            {
                this.RemoveThread(state.LuaThread);
            }

            IntPtr NL;
            int    index;

            lock (_glSyncLock)
            {
                // Create new thread and save index, so it can be removed later.
                NL    = Melua.lua_newthread(GL);
                index = Melua.lua_gettop(GL);
            }

            lock (_states)
                _states.Add(NL, state);

            return(new LuaThread(NL, index));
        }
Пример #7
0
        /// <summary>
        /// Gets or sets a scripting variable.
        /// </summary>
        /// <remarks>
        /// Scripting variables are separate from Lua variables and exist
        /// across script and playing sessions. How the variable is saved
        /// depends on the used prefix.
        ///
        /// Variable names may contain the following characters, apart from
        /// the prefixes, and must start with a character:
        /// abcdefghijklmnopqrstuvwxyz0123456789_
        ///
        /// Prefixes:
        /// ""   - Permanent variable attached to the character.
        /// "@"  - Temporary variable attached to the character.
        /// "#"  - Permanent variable attached to the account.
        /// "$"  - Permanent global variable.
        /// "$@" - Temporary global variable.
        ///
        /// Parameters:
        /// - string variableName
        /// - (optional) T value
        ///
        /// Result:
        /// - T value
        /// </remarks>
        /// <param name="L"></param>
        /// <returns></returns>
        private int var(IntPtr L)
        {
            var conn      = this.GetConnectionFromState(L);
            var character = conn.SelectedCharacter;

            // Get parameters
            var argc = Melua.lua_gettop(L);
            var name = Melua.luaL_checkstring(L, 1).Trim();

            object value = null;

            if (argc == 2)
            {
                if (Melua.lua_isnumber(L, 2))
                {
                    value = Melua.lua_tonumber(L, 2);
                }
                else if (Melua.lua_isstring(L, 2))
                {
                    value = Melua.lua_tostring(L, 2);
                }
                else if (Melua.lua_isboolean(L, 2))
                {
                    value = Melua.lua_toboolean(L, 2);
                }
                else
                {
                    return(Melua.melua_error(L, "Unsupported variable type."));
                }
            }

            Melua.lua_pop(L, argc);

            // Get variable manager and trim name
            VariableManager vars;

            if (name.StartsWith("$@"))
            {
                vars = this.Variables.Temp;
                name = name.Substring(2);
            }
            else if (name.StartsWith("$"))
            {
                vars = this.Variables.Perm;
                name = name.Substring(1);
            }
            else if (name.StartsWith("#"))
            {
                vars = conn.Account.Variables.Perm;
                name = name.Substring(1);
            }
            else if (name.StartsWith("@"))
            {
                vars = character.Variables.Temp;
                name = name.Substring(1);
            }
            else
            {
                vars = character.Variables.Perm;
            }

            // Check name syntax, if we want to add more prefixes later on,
            // we can't have special characters in names.
            if (!VarNameCheck.IsMatch(name))
            {
                return(Melua.melua_error(L, "Invalid variable name."));
            }

            // Update or get value
            if (value == null)
            {
                value = vars[name];
            }
            else
            {
                vars[name] = value;
            }

            // Push return value
            if (value == null)
            {
                Melua.lua_pushnil(L);
            }
            else if (value is string)
            {
                Melua.lua_pushstring(L, (string)value);
            }
            else if (value is double)
            {
                Melua.lua_pushnumber(L, (double)value);
            }
            else if (value is float)
            {
                Melua.lua_pushnumber(L, (float)value);
            }
            else if (value is int)
            {
                Melua.lua_pushinteger(L, (int)value);
            }
            else if (value is bool)
            {
                Melua.lua_pushboolean(L, (bool)value);
            }
            else
            {
                return(Melua.melua_error(L, "Unsupported variable type '{0}'.", value.GetType().Name));
            }

            return(1);
        }
Пример #8
0
        private int DoPath(IntPtr L, string path, bool errorOnMissing)
        {
            var fullPath = path;
            var isHttp   = (path.StartsWith("http://") || path.StartsWith("https://"));
            var status   = 0;

            if (isHttp)
            {
                try
                {
                    var wc     = new WebClient();
                    var script = wc.DownloadString(fullPath);

                    status = Melua.luaL_dostring(L, script);
                }
                catch (WebException ex)
                {
                    if (errorOnMissing)
                    {
                        return(Melua.melua_error(L, "Failed to include remote script '{0}' ({1}).", fullPath, ex.Message));
                    }
                }
            }
            else
            {
                fullPath = Path.GetFullPath(Path.Combine(_cwd, path));

                if (!IsInsideCwd(fullPath))
                {
                    return(Melua.melua_error(L, "Invalid path. ({0})", path));
                }

                if (!File.Exists(fullPath))
                {
                    if (errorOnMissing)
                    {
                        return(Melua.melua_error(L, "File not found. ({0})", path));
                    }
                    else
                    {
                        return(0);
                    }
                }

                status = Melua.luaL_dofile(L, fullPath);
            }

            if (status == 1)             // Error in do/load
            {
                return(Melua.melua_error(L, Melua.lua_tostring(L, -1)));
            }

            if (status != 0)             // Error in execution
            {
                return(status);
            }

            var returnValues = Melua.lua_gettop(L);

            return(returnValues);
        }