[Add] Embedded Lua scripting language.
Over the past couple of commits, the build process now automates the dependenices you'd normally compile from source. This commit is focused on laying the foundation for scriptable in-game commands using Lua. - sol2 and lua5.4 are the new project dependencies. - Created a new 'LuaProcessor' class to manage a Lua state and eecute scripts. - The 'CommandProcessor' now contains a 'LuaProcessor' instance. - Replaced the hardcoded c++ 'ls' command with a system that executes a '/bin/ls.lua' script from the Virtual File System. - Implemented C++ -> Lua bindings for the 'vfs_node' struct, allowing Lua scripts to inspect the VFS and perform actions (i.e, list files).
This commit is contained in:
		
							parent
							
								
									76e61336a1
								
							
						
					
					
						commit
						fbf70c43b3
					
				@ -8,5 +8,6 @@ add_library(bettola
 | 
			
		||||
 | 
			
		||||
target_link_libraries(bettola PUBLIC ${LUA_LIBRARIES} sol2)
 | 
			
		||||
 | 
			
		||||
target_include_directories(bettola PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
 | 
			
		||||
target_include_directories(bettola PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src
 | 
			
		||||
  ${LUA_INCLUDE_DIR})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,14 @@
 | 
			
		||||
#include "command_processor.h"
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
#include "lua_processor.h"
 | 
			
		||||
 | 
			
		||||
CommandProcessor::CommandProcessor(vfs_node* starting_dir) {
 | 
			
		||||
  _current_dir = starting_dir;
 | 
			
		||||
  _lua = new LuaProcessor();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CommandProcessor::~CommandProcessor(void) {
 | 
			
		||||
  delete _lua;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
vfs_node* CommandProcessor::get_current_dir(void) {
 | 
			
		||||
@ -10,6 +16,11 @@ vfs_node* CommandProcessor::get_current_dir(void) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string CommandProcessor::process_command(const std::string& command) {
 | 
			
		||||
  /* Trim trailing whitespace. */
 | 
			
		||||
  std::string cmd = command;
 | 
			
		||||
  size_t end = cmd.find_last_not_of(" \t\n\r");
 | 
			
		||||
  cmd = (end == std::string::npos) ? "" : cmd.substr(0, end+1);
 | 
			
		||||
 | 
			
		||||
  if(command.rfind("cd ", 0) == 0) {
 | 
			
		||||
    std::string target_dir_name = command.substr(3);
 | 
			
		||||
    if(target_dir_name == "..") {
 | 
			
		||||
@ -27,18 +38,27 @@ std::string CommandProcessor::process_command(const std::string& command) {
 | 
			
		||||
      return "cd: no such file or directory\n";
 | 
			
		||||
    }
 | 
			
		||||
    return get_full_path(_current_dir);
 | 
			
		||||
  } else if(command == "ls") {
 | 
			
		||||
    std::string response = "";
 | 
			
		||||
    if(_current_dir && _current_dir->type == DIR_NODE) {
 | 
			
		||||
      for(auto const& [name, node] : _current_dir->children) {
 | 
			
		||||
        response += name;
 | 
			
		||||
        if(node->type == DIR_NODE) {
 | 
			
		||||
          response += "/";
 | 
			
		||||
        }
 | 
			
		||||
        response += "  ";
 | 
			
		||||
  } else if(cmd == "ls") {
 | 
			
		||||
    /* Find the root of the VFS to look for the /bin directory. */
 | 
			
		||||
    vfs_node* root = _current_dir;
 | 
			
		||||
    while(root->parent != nullptr) {
 | 
			
		||||
      root = root->parent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fprintf(stderr, "DEBUG: VFS root found, name: '%s'\n", root->name.c_str());
 | 
			
		||||
 | 
			
		||||
    /* Find and execute the ls.lua script. */
 | 
			
		||||
    if(root->children.count("bin")) {
 | 
			
		||||
      fprintf(stderr, "DEBUG: Found '/bin' directory.\n");
 | 
			
		||||
      vfs_node* bin_dir = root->children["bin"];
 | 
			
		||||
      if(bin_dir->children.count("ls.lua")) {
 | 
			
		||||
        fprintf(stderr, "DEBUG: Found '/bin/ls.lua'. Executing.\n");
 | 
			
		||||
        vfs_node* ls_script_node = bin_dir->children["ls.lua"];
 | 
			
		||||
        return _lua->execute(ls_script_node->content, _current_dir);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return response;
 | 
			
		||||
    fprintf(stderr, "DEBUG: 'ls' command failed to find '/bin/ls.lua'.\n");
 | 
			
		||||
    return "ls: command not found\n";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return "Unknown command: " + command + "\n";
 | 
			
		||||
 | 
			
		||||
@ -3,14 +3,17 @@
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
#include <lua_processor.h>
 | 
			
		||||
 | 
			
		||||
class CommandProcessor {
 | 
			
		||||
public:
 | 
			
		||||
  CommandProcessor(vfs_node* starting_dir);
 | 
			
		||||
  ~CommandProcessor(void);
 | 
			
		||||
 | 
			
		||||
  std::string process_command(const std::string& command);
 | 
			
		||||
  vfs_node* get_current_dir(void);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  vfs_node* _current_dir;
 | 
			
		||||
  LuaProcessor* _lua;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										29
									
								
								common/src/lua_processor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								common/src/lua_processor.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
#include "lua_processor.h"
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
 | 
			
		||||
LuaProcessor::LuaProcessor(void) {
 | 
			
		||||
  _lua.open_libraries(sol::lib::base, sol::lib::string, sol::lib::io);
 | 
			
		||||
 | 
			
		||||
  /* Expose vfs_node struct members to Lua. */
 | 
			
		||||
  _lua.new_usertype<vfs_node>("vfs_node",
 | 
			
		||||
                              "name",       &vfs_node::name,
 | 
			
		||||
                              "type",       &vfs_node::type,
 | 
			
		||||
                              "children",   &vfs_node::children);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LuaProcessor::~LuaProcessor(void) {}
 | 
			
		||||
 | 
			
		||||
std::string LuaProcessor::execute(const std::string& script, vfs_node* current_dir) {
 | 
			
		||||
  try {
 | 
			
		||||
    /* Pass the pointer for the current directory into the Lua env. */
 | 
			
		||||
    _lua["current_dir"] = current_dir;
 | 
			
		||||
    sol::object result = _lua.script(script);
 | 
			
		||||
    if(result.is<std::string>()) {
 | 
			
		||||
      return result.as<std::string>();
 | 
			
		||||
    }
 | 
			
		||||
  } catch(const sol::error& e) {
 | 
			
		||||
    return e.what();
 | 
			
		||||
  }
 | 
			
		||||
  /* Shouldn't reach this, just shutting the compiler up. */
 | 
			
		||||
  return "[Unknown error in LuaProcessor::execute]";
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								common/src/lua_processor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								common/src/lua_processor.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <sol/sol.hpp>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
/* Don't want the full header. */
 | 
			
		||||
struct vfs_node;
 | 
			
		||||
 | 
			
		||||
class LuaProcessor {
 | 
			
		||||
public:
 | 
			
		||||
  LuaProcessor(void);
 | 
			
		||||
  ~LuaProcessor(void);
 | 
			
		||||
 | 
			
		||||
  /* Executes a string of lua code and returns result as a string. */
 | 
			
		||||
  std::string execute(const std::string& script, vfs_node* current_dir);
 | 
			
		||||
private:
 | 
			
		||||
  sol::state _lua;
 | 
			
		||||
};
 | 
			
		||||
@ -29,9 +29,23 @@ vfs_node* vfs_manager::create_root_system(const std::string& system_type) {
 | 
			
		||||
  readme->content = "Welcome to your new virtual machine.";
 | 
			
		||||
  user->children["readme.txt"] = readme;
 | 
			
		||||
 | 
			
		||||
  vfs_node* ls_exe = new_node("ls", FILE_NODE, bin);
 | 
			
		||||
  ls_exe->content = "[executable]";
 | 
			
		||||
  bin->children["ls"] = ls_exe;
 | 
			
		||||
  vfs_node* ls_script = new_node("ls.lua", FILE_NODE, bin);
 | 
			
		||||
  ls_script->content = R"lua(-- /bin/ls.lua - Lists files in a directory.
 | 
			
		||||
                             local dir = current_dir -- Get directory object from C++.
 | 
			
		||||
                             local output = ""
 | 
			
		||||
 | 
			
		||||
                             -- Iterate over the 'children' map exposed from c++.
 | 
			
		||||
                             for name, node in pairs(dir.children) do
 | 
			
		||||
                               output = output .. name
 | 
			
		||||
                               if node.type == 1 then
 | 
			
		||||
                                 output = output .. "/"
 | 
			
		||||
                               end
 | 
			
		||||
                               output = output .. " "
 | 
			
		||||
                             end
 | 
			
		||||
                             return output
 | 
			
		||||
                             )lua";
 | 
			
		||||
 | 
			
		||||
  bin->children["ls.lua"] = ls_script;
 | 
			
		||||
 | 
			
		||||
  if(system_type == "npc") {
 | 
			
		||||
    vfs_node* npc_file = new_node("npc_system.txt", FILE_NODE, root);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user