2012-04-06 3 views
4

Я работаю над улучшением способа обработки Lua-скриптов для роботов в Bitfighter. В настоящее время каждый робот получает свой собственный экземпляр L, и мы пытаемся заставить их использовать общий доступ, заменяя таблицы среды. Обратите внимание, что боты могут быть совершенно разными сценариями.Lua, metatables и глобальные переменные

Я понимаю, что этот метод устарел в Lua 5.2, но в настоящее время мы используем lua-vec, который все еще использует Lua 5.1. Игра написана на C++.

Итак ...

Сначала мы создаем среду, и называют его:

// Create a table with room for 0 array and 1 non-array elements 
lua_createtable(L, 0, 1);     // -- tab 

// Set the globals table to handle any requests that the 
// script's environment can't 
lua_pushstring(L, "__index");    // -- tab, "__index" 
lua_pushvalue(L, LUA_GLOBALSINDEX);  // -- tab, "__index", _G 

// Set table["__index"] = _G, pops top two items from stack 
lua_settable(L, -3);      // -- tab 

// Store the new table in the retistry for future use 
lua_setfield(L, LUA_REGISTRYINDEX, name); // -- <<empty stack>> 

Позже мы загружаем некоторые Lua код и вспомнить среды таблицу:

luaL_loadfile(L, "luascripts.lua"); 

lua_getfield(L, LUA_REGISTRYINDEX, name); // -- function, table 
lua_setfenv(L, -2);      // -- function 

Затем запустите загруженный код:

lua_pcall(L, 0, 0, 0); 

Когда загружен Lua пытается использовать основные функции, такие как печать, он терпит неудачу с ошибкой:

attempt to call global 'print' (a nil value) 

Но сценарий может сделать следующее:

__index["print"](12) 

Так .. почему мы не можем напрямую обращаться к печати? Что нам не хватает? Или существует принципиально лучший способ запуска нескольких сценариев в одном экземпляре Lua?

ответ

3

Ваш код близко к правильному, но содержит несколько вопросов, - Вы пытаетесь сделать что-то, что не будет работать, и ваша попытка сделал неправильно, что в неправильном направлении ..

Вы устанавливаете функцию среда функции в таблицу, которая выглядит следующим образом:

{__index = _G} 

Естественно, что при попытке получить доступ к print, он не найден в этой таблице.

Из ваших комментариев, я делаю вывод, что на самом деле хотел установить поле __indexс метаданными таблицы окружения. То есть, вы хотите, чтобы таблицы окружения быть как t в примере ниже:

t = {} 
setmetatable(t, {__index = _G}) 

(C++ перевод этого довольно просто).

Не делайте этого. Он решит вашу непосредственную проблему, но не обеспечит достаточную песочницу. Рассмотрим, например, сценарий, как это:

table.sort = 10 

"table" будет найден в _G в Метастабильную обработчика событий. sort - это всего лишь элемент таблицы table, и поэтому его можно заменить безнаказанностью. Теперь другие скрипты не смогут сортировать таблицы с table.sort.


Один из способов выполнения такого рода разделения является посредником доступа ко всем глобальным ценностям через какое-то (возможно, рекурсивное) UserData с рукописными обработчиками для соответствующих Метастабильных событий. (Этот способ, вероятно, имеет наибольший потенциал для производительности и контроля, но может быть трудно реализовать).

Другой способ - создать таблицу окружения для каждого сценария и скопировать безопасные/изолированные элементы из глобальной таблицы в эту таблицу окружения (чтобы каждый сценарий имел полностью отдельную версию всех изменяемых элементов глобальная таблица).

Прошу прощения, что у меня нет времени, чтобы правильно объяснить предлагаемые вами решения проблемы. Надеюсь, что я дал вам кое-что начать. Не стесняйтесь возвращаться и редактировать это, чтобы включить решение, которое вы в конечном счете используете.

+0

Спасибо за ваш ответ! «Еще один способ - создать таблицу окружения для каждого скрипта ...», это именно то, что я пытаюсь сделать, хотя я и не думал предоставить каждому сценарию копию таблицы глобальных таблиц. Я вижу из вашего примера, почему это улучшит безопасность и изоляцию скрипта, но я не уверен, как это реализовать, и я не нашел хороших примеров реализаций, несмотря на значительные исследования. – Watusimoto

+0

@Watusimoto: На самом деле это довольно просто. Вы просто скопируете каждый элемент белого списка из глобальной таблицы в таблицу окружения (и когда вы переходите к изменяемым элементам, вы должны правильно скопировать элементы, а не только ссылки). Я вернусь через несколько дней и приведу несколько подходящих примеров. (Пожалуйста, напомните мне, поэтому я не забываю!) – Mankarse

+0

Спасибо. Это должно быть относительно простым, так как моя таблица глобальных таблиц уже ограничена функциями и значениями, к которым я хочу, чтобы скрипты имели доступ, поэтому это будет операция с копией. – Watusimoto

Смежные вопросы