Compare commits
12 Commits
fa7dcdcb0d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 9bb43d2222 | |||
| ce61ea267d | |||
| f7e9305d64 | |||
| 0b5c47d8c7 | |||
| 7d6898edb3 | |||
| da858f4a99 | |||
| 514fca47bc | |||
| 393be51c76 | |||
| 29726631dd | |||
| 7d6b1f207f | |||
| 6a230e3ab2 | |||
| 53a06281fd |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -13,6 +13,7 @@
|
|||||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||||
*.png filter=lfs diff=lfs merge=lfs -text
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
*.psd filter=lfs diff=lfs merge=lfs -text
|
*.psd filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pdf filter=lfs diff=lfs merge=lfs -text
|
||||||
*.sln filter=lfs diff=lfs merge=lfs -text
|
*.sln filter=lfs diff=lfs merge=lfs -text
|
||||||
*.uasset filter=lfs diff=lfs merge=lfs -text
|
*.uasset filter=lfs diff=lfs merge=lfs -text
|
||||||
*.umap filter=lfs diff=lfs merge=lfs -text
|
*.umap filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
[/Script/UnrealEd.EditorStyleSettings]
|
|
||||||
|
|
||||||
AssetEditorOpenLocation=MainWindow
|
|
||||||
|
|
||||||
[/Script/BlueprintGraph.BlueprintEditorSettings]
|
|
||||||
|
|
||||||
SaveOnCompile=SoC_SuccessOnly
|
|
||||||
|
|
||||||
[/Script/UnrealEd.EditorLoadingSavingSettings]
|
|
||||||
|
|
||||||
RestoreOpenAssetTabsOnRestart=AlwaysRestore
|
|
||||||
|
|
||||||
AutoSaveWarningInSeconds=0
|
|
||||||
|
|
||||||
[/Script/Integration.lxProjectSettings]
|
|
||||||
ActiveServer=/Game/Luprex/KnownServers/SS_Localhost.SS_Localhost
|
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,6 +2,7 @@
|
|||||||
#include "BreakToDebugger.h"
|
#include "BreakToDebugger.h"
|
||||||
#include "Blueprint/BlueprintExceptionInfo.h"
|
#include "Blueprint/BlueprintExceptionInfo.h"
|
||||||
#include "Kismet2/KismetDebugUtilities.h"
|
#include "Kismet2/KismetDebugUtilities.h"
|
||||||
|
#include "LuprexEditorSettings.h"
|
||||||
|
|
||||||
ELogVerbosity::Type FlxBreakToDebuggerOutputDevice::ConvertThreshold(ElxBreakToDebuggerThreshold Verbosity) {
|
ELogVerbosity::Type FlxBreakToDebuggerOutputDevice::ConvertThreshold(ElxBreakToDebuggerThreshold Verbosity) {
|
||||||
switch (Verbosity) {
|
switch (Verbosity) {
|
||||||
@@ -15,8 +16,8 @@ ELogVerbosity::Type FlxBreakToDebuggerOutputDevice::ConvertThreshold(ElxBreakToD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FlxBreakToDebuggerOutputDevice::FlxBreakToDebuggerOutputDevice(const ElxBreakToDebuggerThreshold &SensitivityRef)
|
FlxBreakToDebuggerOutputDevice::FlxBreakToDebuggerOutputDevice()
|
||||||
: Sensitivity(SensitivityRef)
|
: Settings(GetDefault<UlxEditorSettings>())
|
||||||
{
|
{
|
||||||
GLog->AddOutputDevice(this);
|
GLog->AddOutputDevice(this);
|
||||||
}
|
}
|
||||||
@@ -48,7 +49,7 @@ void FlxBreakToDebuggerOutputDevice::Serialize(const TCHAR* V, ELogVerbosity::Ty
|
|||||||
{
|
{
|
||||||
// If the error isn't serious enough, do nothing.
|
// If the error isn't serious enough, do nothing.
|
||||||
//
|
//
|
||||||
if (Verbosity > ConvertThreshold(Sensitivity))
|
if (Verbosity > ConvertThreshold(Settings->BreakToDebuggerLogVerbosity))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -67,6 +68,13 @@ void FlxBreakToDebuggerOutputDevice::Serialize(const TCHAR* V, ELogVerbosity::Ty
|
|||||||
UObject *TopObject = Frame->Object;
|
UObject *TopObject = Frame->Object;
|
||||||
if (TopObject == nullptr) return;
|
if (TopObject == nullptr) return;
|
||||||
|
|
||||||
|
// If the category is in the exclude list, do nothing.
|
||||||
|
//
|
||||||
|
if (Settings->BreakToDebuggerExcludeCategories.Contains(Category))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Notify the debugger that there's been an exception.
|
// Notify the debugger that there's been an exception.
|
||||||
//
|
//
|
||||||
FBlueprintExceptionInfo ExceptionInfo(EBlueprintExceptionType::Breakpoint, FText::FromStringView(FStringView(V)));
|
FBlueprintExceptionInfo ExceptionInfo(EBlueprintExceptionType::Breakpoint, FText::FromStringView(FStringView(V)));
|
||||||
|
|||||||
@@ -79,19 +79,15 @@ enum class ElxBreakToDebuggerThreshold : uint8 {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class UlxEditorSettings;
|
||||||
|
|
||||||
struct FlxBreakToDebuggerOutputDevice : public FOutputDevice
|
struct FlxBreakToDebuggerOutputDevice : public FOutputDevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// The constructor and destructor automatically register
|
// The constructor and destructor automatically register
|
||||||
// this output device with GLog.
|
// this output device with GLog.
|
||||||
//
|
//
|
||||||
// This struct doesn't store the sensitivity threshold.
|
FlxBreakToDebuggerOutputDevice();
|
||||||
// It relies on the LuprexGameMode class to do that, so
|
|
||||||
// that the threshold can be easily edited with the
|
|
||||||
// blueprint editor. This struct must be initialized
|
|
||||||
// with a reference to the threshold variable.
|
|
||||||
//
|
|
||||||
FlxBreakToDebuggerOutputDevice(const ElxBreakToDebuggerThreshold &SensitivityRef);
|
|
||||||
~FlxBreakToDebuggerOutputDevice();
|
~FlxBreakToDebuggerOutputDevice();
|
||||||
|
|
||||||
// Inspect a log message.
|
// Inspect a log message.
|
||||||
@@ -109,5 +105,5 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static ELogVerbosity::Type ConvertThreshold(ElxBreakToDebuggerThreshold Verbosity);
|
static ELogVerbosity::Type ConvertThreshold(ElxBreakToDebuggerThreshold Verbosity);
|
||||||
const ElxBreakToDebuggerThreshold &Sensitivity;
|
const UlxEditorSettings *Settings = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,3 +2,4 @@
|
|||||||
|
|
||||||
DEFINE_LOG_CATEGORY(LogLuprex);
|
DEFINE_LOG_CATEGORY(LogLuprex);
|
||||||
DEFINE_LOG_CATEGORY(LogLuprexIntegration);
|
DEFINE_LOG_CATEGORY(LogLuprexIntegration);
|
||||||
|
DEFINE_LOG_CATEGORY(LogLuprexConsole);
|
||||||
|
|||||||
@@ -153,3 +153,7 @@ DECLARE_LOG_CATEGORY_EXTERN(LogLuprex, Display, All);
|
|||||||
// Messages about the Luprex integration with Unreal.
|
// Messages about the Luprex integration with Unreal.
|
||||||
//
|
//
|
||||||
DECLARE_LOG_CATEGORY_EXTERN(LogLuprexIntegration, Display, All);
|
DECLARE_LOG_CATEGORY_EXTERN(LogLuprexIntegration, Display, All);
|
||||||
|
|
||||||
|
// Messages from the Luprex console.
|
||||||
|
//
|
||||||
|
DECLARE_LOG_CATEGORY_EXTERN(LogLuprexConsole, Display, All);
|
||||||
|
|||||||
7
Source/Integration/LuprexEditorSettings.cpp
Normal file
7
Source/Integration/LuprexEditorSettings.cpp
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#include "LuprexEditorSettings.h"
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
UlxEditorSettings::UlxEditorSettings()
|
||||||
|
{
|
||||||
|
BreakToDebuggerExcludeCategories.Add(LogLuprexConsole.GetCategoryName());
|
||||||
|
}
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "Engine/DataAsset.h"
|
#include "Engine/DataAsset.h"
|
||||||
#include "Engine/DeveloperSettings.h"
|
#include "Engine/DeveloperSettings.h"
|
||||||
#include "ProjectSettings.generated.h"
|
#include "BreakToDebugger.h"
|
||||||
|
#include "LuprexEditorSettings.generated.h"
|
||||||
|
|
||||||
UCLASS(BlueprintType)
|
UCLASS(BlueprintType)
|
||||||
class INTEGRATION_API UlxServerSelection : public UPrimaryDataAsset
|
class INTEGRATION_API UlxServerSelection : public UPrimaryDataAsset
|
||||||
@@ -18,17 +19,31 @@ public:
|
|||||||
FString Host;
|
FString Host;
|
||||||
};
|
};
|
||||||
|
|
||||||
UCLASS(Config=EditorPerProjectUserSettings, DefaultConfig, meta=(DisplayName="Server Settings"))
|
UCLASS(Config=EditorPerProjectUserSettings, DefaultConfig, meta=(DisplayName="Luprex Editor Settings"))
|
||||||
class INTEGRATION_API UlxProjectSettings : public UDeveloperSettings
|
class INTEGRATION_API UlxEditorSettings : public UDeveloperSettings
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
UlxEditorSettings();
|
||||||
|
|
||||||
virtual FName GetContainerName() const override { return TEXT("Editor"); }
|
virtual FName GetContainerName() const override { return TEXT("Editor"); }
|
||||||
virtual FName GetCategoryName() const override { return TEXT("Luprex"); }
|
virtual FName GetCategoryName() const override { return TEXT("Luprex"); }
|
||||||
virtual FName GetSectionName() const override { return TEXT("Server"); }
|
virtual FName GetSectionName() const override { return TEXT("Settings"); }
|
||||||
|
|
||||||
|
// The server to connect to. You can add an option to the list by creating
|
||||||
|
// a new data asset of type 'lx Server Selection'.
|
||||||
UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category="Server",
|
UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category="Server",
|
||||||
meta=(AllowedClasses="/Script/Integration.lxServerSelection"))
|
meta=(AllowedClasses="/Script/Integration.lxServerSelection"))
|
||||||
TSoftObjectPtr<UlxServerSelection> ActiveServer;
|
TSoftObjectPtr<UlxServerSelection> ActiveServer;
|
||||||
|
|
||||||
|
// If blueprint execution generates a log message of at least the following
|
||||||
|
// severity, the blueprint debugger will automatically pause with a breakpoint.
|
||||||
|
UPROPERTY(Config, EditAnywhere, Category="Debugging Tools")
|
||||||
|
ElxBreakToDebuggerThreshold BreakToDebuggerLogVerbosity;
|
||||||
|
|
||||||
|
// Log categories in this set are excluded from BreakToDebugger triggering.
|
||||||
|
UPROPERTY(Config, EditAnywhere, Category="Debugging Tools")
|
||||||
|
TSet<FName> BreakToDebuggerExcludeCategories;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
#include "Blueprint/WidgetBlueprintLibrary.h"
|
#include "Blueprint/WidgetBlueprintLibrary.h"
|
||||||
#include "Kismet/GameplayStatics.h"
|
#include "Kismet/GameplayStatics.h"
|
||||||
#include "Engine/GameInstance.h"
|
#include "Engine/GameInstance.h"
|
||||||
#include "ProjectSettings.h"
|
#include "LuprexEditorSettings.h"
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "AnimQueue.h"
|
#include "AnimQueue.h"
|
||||||
@@ -215,7 +215,7 @@ void ALuprexGameModeBase::InitializeGlobalState()
|
|||||||
|
|
||||||
// Possibly tell the engine to connect to a server.
|
// Possibly tell the engine to connect to a server.
|
||||||
if (Playing) {
|
if (Playing) {
|
||||||
UlxServerSelection* Server = GetDefault<UlxProjectSettings>()->ActiveServer.LoadSynchronous();
|
UlxServerSelection* Server = GetDefault<UlxEditorSettings>()->ActiveServer.LoadSynchronous();
|
||||||
if (Server) {
|
if (Server) {
|
||||||
FString Host = Server->Host.TrimStartAndEnd();
|
FString Host = Server->Host.TrimStartAndEnd();
|
||||||
if (!Host.IsEmpty()) {
|
if (!Host.IsEmpty()) {
|
||||||
@@ -238,7 +238,7 @@ void ALuprexGameModeBase::InitializeGlobalState()
|
|||||||
|
|
||||||
// If somebody generates a log message that's severe enough, break to debugger.
|
// If somebody generates a log message that's severe enough, break to debugger.
|
||||||
BreakToDebuggerLogVerbosityDevice.Reset(
|
BreakToDebuggerLogVerbosityDevice.Reset(
|
||||||
new FlxBreakToDebuggerOutputDevice(BreakToDebuggerLogVerbosity));
|
new FlxBreakToDebuggerOutputDevice());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALuprexGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
void ALuprexGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||||
|
|||||||
@@ -68,10 +68,6 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable, Category = "Luprex|Miscellaneous", meta = (WorldContext = "WorldContextObject"))
|
UFUNCTION(BlueprintCallable, Category = "Luprex|Miscellaneous", meta = (WorldContext = "WorldContextObject"))
|
||||||
static void TriggerReloadSource(const UObject *WorldContextObject);
|
static void TriggerReloadSource(const UObject *WorldContextObject);
|
||||||
|
|
||||||
// The sensitivity level at which a log message triggers a debugger breakpoint.
|
|
||||||
UPROPERTY(EditAnywhere, Category="Debugging Tools")
|
|
||||||
ElxBreakToDebuggerThreshold BreakToDebuggerLogVerbosity;
|
|
||||||
|
|
||||||
// Luprex socket system.
|
// Luprex socket system.
|
||||||
TUniquePtr<FlxSockets> Sockets;
|
TUniquePtr<FlxSockets> Sockets;
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
#include "ProjectSettings.h"
|
|
||||||
@@ -242,3 +242,8 @@ void UlxUtilityLibrary::ValidateLuaExpr(
|
|||||||
Status = w.ValidateLuaExpr(Code, ErrorMessage);
|
Status = w.ValidateLuaExpr(Code, ErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UlxUtilityLibrary::LogLuprexConsoleMessage(const FString &Message)
|
||||||
|
{
|
||||||
|
UE_LOG(LogLuprexConsole, Warning, TEXT("%s"), *Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -144,4 +144,9 @@ public:
|
|||||||
//
|
//
|
||||||
UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Utility")
|
UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Utility")
|
||||||
static void ValidateLuaExpr(ElxLuaSyntaxCheck &Status, FString &ErrorMessage, UObject *context, const FString &Code);
|
static void ValidateLuaExpr(ElxLuaSyntaxCheck &Status, FString &ErrorMessage, UObject *context, const FString &Code);
|
||||||
|
|
||||||
|
// Log a message to the LogLuprexConsole category.
|
||||||
|
//
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Luprex|Utility")
|
||||||
|
static void LogLuprexConsoleMessage(const FString &Message);
|
||||||
};
|
};
|
||||||
|
|||||||
4
TeppyNotes.txt
Normal file
4
TeppyNotes.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
- To run a server:
|
||||||
|
- cd luprex
|
||||||
|
- ./build/Linux/luprexstatic lpxserver
|
||||||
|
|
||||||
@@ -18,6 +18,8 @@ public:
|
|||||||
PrintChanneler print_channeler_;
|
PrintChanneler print_channeler_;
|
||||||
eng::vector<Invocation> delayed_invocations_;
|
eng::vector<Invocation> delayed_invocations_;
|
||||||
lua_State *lua_syntax_checker_;
|
lua_State *lua_syntax_checker_;
|
||||||
|
eng::string auto_reconnect_target_;
|
||||||
|
double auto_reconnect_time_ = 0.0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LpxClient(EngineWrapper *w) : wrapper_(w) {
|
LpxClient(EngineWrapper *w) : wrapper_(w) {
|
||||||
@@ -58,6 +60,10 @@ public:
|
|||||||
|
|
||||||
// Clear any saved invocations
|
// Clear any saved invocations
|
||||||
delayed_invocations_.clear();
|
delayed_invocations_.clear();
|
||||||
|
|
||||||
|
// Clear auto-reconnect state.
|
||||||
|
auto_reconnect_target_.clear();
|
||||||
|
auto_reconnect_time_ = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_initial_state_standalone() {
|
void set_initial_state_standalone() {
|
||||||
@@ -83,6 +89,10 @@ public:
|
|||||||
|
|
||||||
// Clear any saved invocations
|
// Clear any saved invocations
|
||||||
delayed_invocations_.clear();
|
delayed_invocations_.clear();
|
||||||
|
|
||||||
|
// Clear auto-reconnect state.
|
||||||
|
auto_reconnect_target_.clear();
|
||||||
|
auto_reconnect_time_ = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the world is in synchronous mode, there's no
|
// When the world is in synchronous mode, there's no
|
||||||
@@ -108,10 +118,12 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void abandon_server() {
|
void disconnect_and_reconnect() {
|
||||||
if (channel_)
|
if (channel_)
|
||||||
{
|
{
|
||||||
set_initial_state_standalone();
|
auto_reconnect_target_ = channel_->target();
|
||||||
|
auto_reconnect_time_ = get_clock() + 5.0;
|
||||||
|
channel_.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +150,7 @@ public:
|
|||||||
// An ack is just a single byte, so there's nothing left to read.
|
// An ack is just a single byte, so there's nothing left to read.
|
||||||
if (unack_.empty()) {
|
if (unack_.empty()) {
|
||||||
// Invalid acknowledgement when theres' nothing in the unack queue.
|
// Invalid acknowledgement when theres' nothing in the unack queue.
|
||||||
abandon_server();
|
disconnect_and_reconnect();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
world_to_synchronous();
|
world_to_synchronous();
|
||||||
@@ -158,7 +170,7 @@ public:
|
|||||||
}
|
}
|
||||||
// dbc.dump(...);
|
// dbc.dump(...);
|
||||||
} catch (const StreamException &sexcept) {
|
} catch (const StreamException &sexcept) {
|
||||||
abandon_server();
|
disconnect_and_reconnect();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,11 +191,11 @@ public:
|
|||||||
} else if (message_type == util::MSG_DIFF) {
|
} else if (message_type == util::MSG_DIFF) {
|
||||||
receive_diff_from_server(&body);
|
receive_diff_from_server(&body);
|
||||||
} else {
|
} else {
|
||||||
abandon_server();
|
disconnect_and_reconnect();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!body.empty()) {
|
if (!body.empty()) {
|
||||||
abandon_server();
|
disconnect_and_reconnect();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -248,7 +260,7 @@ public:
|
|||||||
if (channel_ != nullptr) {
|
if (channel_ != nullptr) {
|
||||||
if (channel_->closed()) {
|
if (channel_->closed()) {
|
||||||
util::dprint("server closed connection: ", channel_->error());
|
util::dprint("server closed connection: ", channel_->error());
|
||||||
abandon_server();
|
disconnect_and_reconnect();
|
||||||
} else {
|
} else {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (!receive_message_from_server(channel_->in())) break;
|
if (!receive_message_from_server(channel_->in())) break;
|
||||||
@@ -256,6 +268,10 @@ public:
|
|||||||
}
|
}
|
||||||
world_to_asynchronous();
|
world_to_asynchronous();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if ((!auto_reconnect_target_.empty()) && (get_clock() > auto_reconnect_time_)) {
|
||||||
|
set_initial_state_connect(auto_reconnect_target_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set_have_prints(print_channeler_.have_prints(world_->get_printbuffer(actor_id_)));
|
set_have_prints(print_channeler_.have_prints(world_->get_printbuffer(actor_id_)));
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ lua_State *LuaCoreStack::newstate (lua_Alloc allocf) {
|
|||||||
LS.rawset(LuaRegistry, "persist", LuaNewTable);
|
LS.rawset(LuaRegistry, "persist", LuaNewTable);
|
||||||
LS.rawset(LuaRegistry, "unpersist", LuaNewTable);
|
LS.rawset(LuaRegistry, "unpersist", LuaNewTable);
|
||||||
LS.rawset(LuaRegistry, "funcnames", LuaNewTable);
|
LS.rawset(LuaRegistry, "funcnames", LuaNewTable);
|
||||||
|
LS.rawset(LuaRegistry, "sourcedb", LuaNewTable);
|
||||||
|
|
||||||
// Tag the registry and global environment with their tabletypes.
|
// Tag the registry and global environment with their tabletypes.
|
||||||
LS.settabletype(LuaRegistry, LUA_TT_REGISTRY);
|
LS.settabletype(LuaRegistry, LUA_TT_REGISTRY);
|
||||||
|
|||||||
@@ -171,6 +171,7 @@ public:
|
|||||||
LuaExtraArgs(int i, int s) : index_(i), size_(s) {}
|
LuaExtraArgs(int i, int s) : index_(i), size_(s) {}
|
||||||
|
|
||||||
LuaSpecial operator[] (int n) const { return LuaSpecial(index_ + n); }
|
LuaSpecial operator[] (int n) const { return LuaSpecial(index_ + n); }
|
||||||
|
int index(int n) const { return index_ + n; }
|
||||||
int size() const { return size_; }
|
int size() const { return size_; }
|
||||||
|
|
||||||
friend class LuaCoreStack;
|
friend class LuaCoreStack;
|
||||||
|
|||||||
@@ -132,16 +132,6 @@ static void get_info_table(LuaCoreStack &LS, LuaSlot db, LuaSlot info, const eng
|
|||||||
LS.rawset(info, "name", fn);
|
LS.rawset(info, "name", fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void calculate_loadresult(LuaCoreStack &LS0, LuaSlot info, const eng::string &fn, const eng::string &code) {
|
|
||||||
LuaVar loadresult;
|
|
||||||
LuaExtStack LS(LS0.state(), loadresult);
|
|
||||||
if (code == "") {
|
|
||||||
LS.rawset(info, "loadresult", "missing or empty source file");
|
|
||||||
} else {
|
|
||||||
LS.load(loadresult, code, fn);
|
|
||||||
LS.rawset(info, "loadresult", loadresult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SourceDB::diff(const SourceDB &auth, StreamBuffer *sb) {
|
void SourceDB::diff(const SourceDB &auth, StreamBuffer *sb) {
|
||||||
LuaVar sdb, sfn, sinfo, shash, sseq;
|
LuaVar sdb, sfn, sinfo, shash, sseq;
|
||||||
@@ -202,8 +192,8 @@ void SourceDB::diff(const SourceDB &auth, StreamBuffer *sb) {
|
|||||||
|
|
||||||
bool SourceDB::patch(StreamBuffer *sb, DebugCollector *dbc) {
|
bool SourceDB::patch(StreamBuffer *sb, DebugCollector *dbc) {
|
||||||
lua_State *L = lua_state_;
|
lua_State *L = lua_state_;
|
||||||
LuaVar db, info;
|
LuaVar db, info, closure;
|
||||||
LuaExtStack LS(L, db, info);
|
LuaExtStack LS(L, db, info, closure);
|
||||||
LS.rawget(db, LuaRegistry, "sourcedb");
|
LS.rawget(db, LuaRegistry, "sourcedb");
|
||||||
int nupdates = sb->read_int32();
|
int nupdates = sb->read_int32();
|
||||||
for (int i = 0; i < nupdates; i++) {
|
for (int i = 0; i < nupdates; i++) {
|
||||||
@@ -219,24 +209,27 @@ bool SourceDB::patch(StreamBuffer *sb, DebugCollector *dbc) {
|
|||||||
if (code != "\001") {
|
if (code != "\001") {
|
||||||
LS.rawset(info, "code", code);
|
LS.rawset(info, "code", code);
|
||||||
LS.rawset(info, "hash", util::hash_to_hex(util::hash_string(code)));
|
LS.rawset(info, "hash", util::hash_to_hex(util::hash_string(code)));
|
||||||
calculate_loadresult(LS, info, fn, code);
|
LS.load(closure, code, fn);
|
||||||
|
LS.rawset(info, "loadresult", closure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (nupdates > 0) rebuild();
|
||||||
return (nupdates > 0);
|
return (nupdates > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SourceDB::set(const eng::string &fn, const eng::string &code, int sequence) {
|
void SourceDB::set(const eng::string &fn, const eng::string &code, int sequence) {
|
||||||
lua_State *L = lua_state_;
|
lua_State *L = lua_state_;
|
||||||
LuaVar db, info;
|
LuaVar db, info, closure;
|
||||||
LuaExtStack LS(L, db, info);
|
LuaExtStack LS(L, db, info, closure);
|
||||||
LS.rawget(db, LuaRegistry, "sourcedb");
|
LS.rawget(db, LuaRegistry, "sourcedb");
|
||||||
get_info_table(LS, db, info, fn);
|
get_info_table(LS, db, info, fn);
|
||||||
LS.rawset(info, "sequence", sequence);
|
LS.rawset(info, "sequence", sequence);
|
||||||
LS.rawset(info, "code", code);
|
LS.rawset(info, "code", code);
|
||||||
LS.rawset(info, "fingerprint", "");
|
LS.rawset(info, "fingerprint", "");
|
||||||
LS.rawset(info, "hash", util::hash_to_hex(util::hash_string(code)));
|
LS.rawset(info, "hash", util::hash_to_hex(util::hash_string(code)));
|
||||||
calculate_loadresult(LS, info, fn, code);
|
LS.load(closure, code, fn);
|
||||||
|
LS.rawset(info, "loadresult", closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
eng::string SourceDB::get(const eng::string &fn) {
|
eng::string SourceDB::get(const eng::string &fn) {
|
||||||
@@ -262,19 +255,15 @@ eng::string SourceDB::get(const eng::string &fn) {
|
|||||||
return util::ss(seqno, ":", ccode, ":", cloadresult);
|
return util::ss(seqno, ":", ccode, ":", cloadresult);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SourceDB::update(const util::LuaSourceVec &source) {
|
eng::string SourceDB::update(const util::LuaSourceVec &source) {
|
||||||
lua_State *L = lua_state_;
|
lua_State *L = lua_state_;
|
||||||
LuaVar sourcedb, info;
|
LuaVar newsourcedb, oldsourcedb, info, closure;
|
||||||
LuaExtStack LS(L, sourcedb, info);
|
LuaExtStack LS(L, newsourcedb, oldsourcedb, info, closure);
|
||||||
|
|
||||||
// Get and clear the source database.
|
eng::ostringstream errors;
|
||||||
LS.rawget(sourcedb, LuaRegistry, "sourcedb");
|
|
||||||
if (!LS.istable(sourcedb)) {
|
|
||||||
LS.newtable(sourcedb);
|
|
||||||
LS.rawset(LuaRegistry, "sourcedb", sourcedb);
|
|
||||||
}
|
|
||||||
LS.cleartable(sourcedb, true);
|
|
||||||
|
|
||||||
|
// Step one. Build the new sourcedb.
|
||||||
|
LS.newtable(newsourcedb);
|
||||||
for (int i = 0; i < int(source.size()); i++) {
|
for (int i = 0; i < int(source.size()); i++) {
|
||||||
const eng::string &file = source[i].first;
|
const eng::string &file = source[i].first;
|
||||||
const eng::string &code = source[i].second;
|
const eng::string &code = source[i].second;
|
||||||
@@ -283,9 +272,32 @@ void SourceDB::update(const util::LuaSourceVec &source) {
|
|||||||
LS.rawset(info, "code", code);
|
LS.rawset(info, "code", code);
|
||||||
LS.rawset(info, "hash", util::hash_to_hex(util::hash_string(code)));
|
LS.rawset(info, "hash", util::hash_to_hex(util::hash_string(code)));
|
||||||
LS.rawset(info, "sequence", i + 1);
|
LS.rawset(info, "sequence", i + 1);
|
||||||
calculate_loadresult(LS, info, file, code);
|
LS.load(closure, code, file);
|
||||||
LS.rawset(sourcedb, file, info);
|
LS.rawset(info, "loadresult", closure);
|
||||||
|
if (LS.isstring(closure)) {
|
||||||
|
errors << file << ":" << LS.ckstring(closure) << std::endl;
|
||||||
|
}
|
||||||
|
LS.rawset(newsourcedb, file, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there were compile errors, return the errors and don't update the env.
|
||||||
|
if (!errors.view().empty()) {
|
||||||
|
return errors.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache the old source database and store the new one.
|
||||||
|
LS.rawget(oldsourcedb, LuaRegistry, "sourcedb");
|
||||||
|
LS.rawset(LuaRegistry, "sourcedb", newsourcedb);
|
||||||
|
|
||||||
|
// Rebuild the source database.
|
||||||
|
eng::string rebuild_errors = rebuild();
|
||||||
|
|
||||||
|
// Restore the old state, if there were errors.
|
||||||
|
if (!rebuild_errors.empty()) {
|
||||||
|
LS.rawset(LuaRegistry, "sourcedb", oldsourcedb);
|
||||||
|
rebuild();
|
||||||
|
}
|
||||||
|
return rebuild_errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete everything from the global environment.
|
// Delete everything from the global environment.
|
||||||
@@ -411,14 +423,11 @@ eng::string SourceDB::get_source(const eng::string &fn)
|
|||||||
return LS.ckstring(code);
|
return LS.ckstring(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
eng::vector<eng::string> SourceDB::modules() {
|
eng::vector<eng::string> SourceDB::modules() {
|
||||||
eng::vector<eng::string> result;
|
eng::vector<eng::string> result;
|
||||||
LuaVar sourcedb, key, info, seq;
|
LuaVar sourcedb, key, info, seq;
|
||||||
LuaExtStack LS(lua_state_, sourcedb, key, info, seq);
|
LuaExtStack LS(lua_state_, sourcedb, key, info, seq);
|
||||||
|
|
||||||
result.push_back("CORE");
|
|
||||||
|
|
||||||
// Get the source database.
|
// Get the source database.
|
||||||
LS.rawget(sourcedb, LuaRegistry, "sourcedb");
|
LS.rawget(sourcedb, LuaRegistry, "sourcedb");
|
||||||
if (!LS.istable(sourcedb)) {
|
if (!LS.istable(sourcedb)) {
|
||||||
@@ -439,35 +448,38 @@ eng::vector<eng::string> SourceDB::modules() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SourceDB::rebuild_core() {
|
eng::string SourceDB::rebuild_module(const eng::string &mod) {
|
||||||
|
LuaVar sourcedb, info, closure;
|
||||||
|
LuaExtStack LS(lua_state_, sourcedb, info, closure);
|
||||||
|
LS.rawget(sourcedb, LuaRegistry, "sourcedb");
|
||||||
|
LS.rawget(info, sourcedb, mod);
|
||||||
|
if (!LS.istable(info)) {
|
||||||
|
return util::ss("No such module: ", mod);
|
||||||
|
}
|
||||||
|
LS.rawget(closure, info, "loadresult");
|
||||||
|
if (!LS.isfunction(closure)) {
|
||||||
|
return util::ss(mod, ":", LS.ckstring(closure));
|
||||||
|
}
|
||||||
|
lua_pushvalue(lua_state_, closure.index());
|
||||||
|
return traceback_pcall(lua_state_, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
eng::string SourceDB::rebuild() {
|
||||||
source_clear_globals(lua_state_);
|
source_clear_globals(lua_state_);
|
||||||
source_load_cfunctions(lua_state_);
|
source_load_cfunctions(lua_state_);
|
||||||
source_load_cconstants(lua_state_);
|
source_load_cconstants(lua_state_);
|
||||||
|
|
||||||
|
eng::ostringstream errors;
|
||||||
|
|
||||||
|
for (const eng::string &mod: modules()) {
|
||||||
|
eng::string err = rebuild_module(mod);
|
||||||
|
if (!err.empty()) errors << err << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
rebuild_funcnames();
|
||||||
|
return errors.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
eng::string SourceDB::rebuild_module(const eng::string &mod) {
|
|
||||||
if (mod == "CORE") {
|
|
||||||
rebuild_core();
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
LuaVar sourcedb, info, closure;
|
|
||||||
LuaExtStack LS(lua_state_, sourcedb, info, closure);
|
|
||||||
LS.rawget(sourcedb, LuaRegistry, "sourcedb");
|
|
||||||
if (!LS.istable(sourcedb)) {
|
|
||||||
return "SourceDB not initialized";
|
|
||||||
}
|
|
||||||
LS.rawget(info, sourcedb, mod);
|
|
||||||
if (!LS.istable(info)) {
|
|
||||||
return util::ss("No such module: ", mod);
|
|
||||||
}
|
|
||||||
LS.rawget(closure, info, "loadresult");
|
|
||||||
if (!LS.isfunction(closure)) {
|
|
||||||
return util::ss(mod, ":", LS.ckstring(closure));
|
|
||||||
}
|
|
||||||
lua_pushvalue(lua_state_, closure.index());
|
|
||||||
return traceback_pcall(lua_state_, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SourceDB::init(lua_State *L) {
|
void SourceDB::init(lua_State *L) {
|
||||||
register_lua_builtins();
|
register_lua_builtins();
|
||||||
@@ -482,8 +494,7 @@ void SourceDB::init(lua_State *L) {
|
|||||||
LS.set(nullstring, "");
|
LS.set(nullstring, "");
|
||||||
LS.setmetatable(nullstring, classtab);
|
LS.setmetatable(nullstring, classtab);
|
||||||
|
|
||||||
// Rebuild the global environment.
|
rebuild();
|
||||||
rebuild_core();
|
|
||||||
|
|
||||||
// We need to register all C functions with the eris permanents tables.
|
// We need to register all C functions with the eris permanents tables.
|
||||||
LS.rawget(persist, LuaRegistry, "persist");
|
LS.rawget(persist, LuaRegistry, "persist");
|
||||||
@@ -516,15 +527,22 @@ void SourceDB::serialize_source(const util::LuaSourceVec &sv, StreamBuffer *sb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SourceDB::deserialize_source(util::LuaSourceVec *sv, StreamBuffer *sb) {
|
util::LuaSourceVec SourceDB::deserialize_source(std::string_view datapack) {
|
||||||
sv->clear();
|
StreamBuffer sb(datapack);
|
||||||
int count = sb->read_int32();
|
util::LuaSourceVec sv;
|
||||||
if ((count < 0) || (count > 10000)) throw StreamCorruption();
|
try {
|
||||||
for (int i = 0; i < count; i++) {
|
sv.clear();
|
||||||
eng::string fn = sb->read_string();
|
int count = sb.read_int32();
|
||||||
eng::string code = sb->read_string();
|
if ((count < 0) || (count > 10000)) throw StreamCorruption();
|
||||||
sv->emplace_back(fn, code);
|
for (int i = 0; i < count; i++) {
|
||||||
|
eng::string fn = sb.read_string();
|
||||||
|
eng::string code = sb.read_string();
|
||||||
|
sv.emplace_back(fn, code);
|
||||||
|
}
|
||||||
|
} catch (const StreamException &ex) {
|
||||||
|
sv.clear();
|
||||||
}
|
}
|
||||||
|
return sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register lua builtins.
|
// Register lua builtins.
|
||||||
@@ -630,7 +648,6 @@ bool SourceDB::search_docs(const eng::string &substring, std::ostream &ostream)
|
|||||||
|
|
||||||
// Search the lua source code.
|
// Search the lua source code.
|
||||||
for (const eng::string &module : modules()) {
|
for (const eng::string &module : modules()) {
|
||||||
if (module == "CORE") continue;
|
|
||||||
eng::string code = get_source(module);
|
eng::string code = get_source(module);
|
||||||
if (code.empty()) continue;
|
if (code.empty()) continue;
|
||||||
util::StringVec lines = util::split_lines(code);
|
util::StringVec lines = util::split_lines(code);
|
||||||
|
|||||||
@@ -132,58 +132,31 @@ private:
|
|||||||
public:
|
public:
|
||||||
void init(lua_State *L);
|
void init(lua_State *L);
|
||||||
|
|
||||||
// Update
|
// modules()
|
||||||
//
|
//
|
||||||
// Update the database using the specified lua source code.
|
// Returns a list of all the lua modules, in the proper order.
|
||||||
// Compiles these files using lua's "load" function.
|
|
||||||
//
|
|
||||||
void update(const util::LuaSourceVec &source);
|
|
||||||
|
|
||||||
// modules
|
|
||||||
//
|
|
||||||
// Returns a list of all the modules. The first item in the list
|
|
||||||
// is always the string "CORE" which represents the lua core
|
|
||||||
// functionality with all the builtins. This is then followed by
|
|
||||||
// all the lua sourcefiles in the correct order.
|
|
||||||
//
|
//
|
||||||
eng::vector<eng::string> modules();
|
eng::vector<eng::string> modules();
|
||||||
|
// Update
|
||||||
// get_source
|
|
||||||
//
|
//
|
||||||
// Get the source code for a given module.
|
// Try to compile and load the specified source. Then, rebuild the
|
||||||
|
// global environment.
|
||||||
//
|
//
|
||||||
eng::string get_source(const eng::string &fn);
|
// If this generates any errors, puts back the old code, and rebuilds
|
||||||
|
// the global environment using the old code.
|
||||||
// rebuild_module
|
//
|
||||||
|
// Returns any error messages. If this returns empty string, it means
|
||||||
|
// there were no errors and the code was successfully update. If there
|
||||||
|
// are any error messages, it means we restored the old code as best as
|
||||||
|
// possible.
|
||||||
//
|
//
|
||||||
// To rebuild the lua environment, fetch the module list, then
|
eng::string update(const util::LuaSourceVec &source);
|
||||||
// call rebuild_module on each module in turn. This will return
|
|
||||||
// an error message for the module, or empty string if no error.
|
|
||||||
//
|
|
||||||
// This is a thin wrapper around traceback_pcall. The return
|
|
||||||
// value is the return value of traceback_pcall.
|
|
||||||
//
|
|
||||||
eng::string rebuild_module(const eng::string &mod);
|
|
||||||
|
|
||||||
// rebuild_core
|
|
||||||
//
|
|
||||||
// This is equivalent to rebuild_module("CORE"). Clears the environment
|
|
||||||
// and installs all the builtins. No error conditions.
|
|
||||||
//
|
|
||||||
void rebuild_core();
|
|
||||||
|
|
||||||
// rebuild_funcnames
|
|
||||||
//
|
|
||||||
// Traverses the global environment and populates the registry "funcnames"
|
|
||||||
// table, mapping each closure to its name.
|
|
||||||
//
|
|
||||||
void rebuild_funcnames();
|
|
||||||
|
|
||||||
// Difference transmission.
|
// Difference transmission.
|
||||||
//
|
//
|
||||||
// Note: The patch routine applies the differences to the source
|
// Note: The patch routine applies the differences to the source
|
||||||
// database, and if there are any changes, it does a source rebuild.
|
// database, and if there are any changes, it does a source rebuild.
|
||||||
// The patch routine returns true if anything was modified.
|
//
|
||||||
//
|
//
|
||||||
void diff(const SourceDB &auth, StreamBuffer *sb);
|
void diff(const SourceDB &auth, StreamBuffer *sb);
|
||||||
bool patch(StreamBuffer *sb, DebugCollector *dbc);
|
bool patch(StreamBuffer *sb, DebugCollector *dbc);
|
||||||
@@ -213,7 +186,54 @@ public:
|
|||||||
// Serialize and unserialize a source vector.
|
// Serialize and unserialize a source vector.
|
||||||
//
|
//
|
||||||
static void serialize_source(const util::LuaSourceVec &sv, StreamBuffer *sb);
|
static void serialize_source(const util::LuaSourceVec &sv, StreamBuffer *sb);
|
||||||
static void deserialize_source(util::LuaSourceVec *sv, StreamBuffer *sb);
|
static util::LuaSourceVec deserialize_source(std::string_view datapack);
|
||||||
|
|
||||||
|
private:
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Internal implementation stuff.
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// rebuild
|
||||||
|
//
|
||||||
|
// Rebuild the global environment from the sourcedb:
|
||||||
|
//
|
||||||
|
// * Clears the environment
|
||||||
|
// * Installs the builtins
|
||||||
|
// * Executes all the closures in the sourcedb.
|
||||||
|
// * Regenerates the function-names table.
|
||||||
|
//
|
||||||
|
// The closures may generate errors, if so, this returns the error
|
||||||
|
// messages. Note that if there are errors, there is no automatic cleanup.
|
||||||
|
//
|
||||||
|
eng::string rebuild();
|
||||||
|
|
||||||
|
// get_source
|
||||||
|
//
|
||||||
|
// Get the source code for a given module.
|
||||||
|
//
|
||||||
|
eng::string get_source(const eng::string &fn);
|
||||||
|
|
||||||
|
// rebuild_module
|
||||||
|
//
|
||||||
|
// To rebuild the lua environment, fetch the module list, then
|
||||||
|
// call rebuild_module on each module in turn. This will return
|
||||||
|
// an error message for the module, or empty string if no error.
|
||||||
|
//
|
||||||
|
// This is a thin wrapper around traceback_pcall. The return
|
||||||
|
// value is the return value of traceback_pcall.
|
||||||
|
//
|
||||||
|
eng::string rebuild_module(const eng::string &mod);
|
||||||
|
|
||||||
|
|
||||||
|
// rebuild_funcnames
|
||||||
|
//
|
||||||
|
// Traverses the global environment and populates the registry "funcnames"
|
||||||
|
// table, mapping each closure to its name.
|
||||||
|
//
|
||||||
|
void rebuild_funcnames();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ public:
|
|||||||
virtual void event_access(AccessKind kind, int64_t place_id, std::string_view datapk, StreamBuffer *retpk) override {
|
virtual void event_access(AccessKind kind, int64_t place_id, std::string_view datapk, StreamBuffer *retpk) override {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case AccessKind::INVOKE_LUA_SOURCE: {
|
case AccessKind::INVOKE_LUA_SOURCE: {
|
||||||
world_->update_source(datapk, 0);
|
Invocation inv(kind, place_id, place_id, datapk);
|
||||||
|
world_->invoke(0, inv);
|
||||||
run_unittests(world_->state());
|
run_unittests(world_->state());
|
||||||
stop_driver();
|
stop_driver();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -607,20 +607,13 @@ LuaDefine(tangible_start, "tangible,function,arg1,arg2...",
|
|||||||
"|to continue running until it blocks. But from that point"
|
"|to continue running until it blocks. But from that point"
|
||||||
"|forward, the thread will never be resumed for any reason.") {
|
"|forward, the thread will never be resumed for any reason.") {
|
||||||
|
|
||||||
int top = lua_gettop(L);
|
|
||||||
if (top < 2) {
|
|
||||||
luaL_error(L, "Not enough arguments to tangible.start");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int varlen = top - 2;
|
|
||||||
|
|
||||||
World *w = World::fetch_global_pointer(L);
|
World *w = World::fetch_global_pointer(L);
|
||||||
w->guard_blockable(L, "tangible.start");
|
w->guard_blockable(L, "tangible.start");
|
||||||
|
|
||||||
|
LuaArg place, fname;
|
||||||
|
LuaExtraArgs extra;
|
||||||
LuaVar mt, classtab, plthreads, thread, thinfo, func, tanlist;
|
LuaVar mt, classtab, plthreads, thread, thinfo, func, tanlist;
|
||||||
LuaDefStack LS(L, mt, classtab, plthreads, thread, thinfo, func, tanlist);
|
LuaDefStack LS(L, place, fname, extra, mt, classtab, plthreads, thread, thinfo, func, tanlist);
|
||||||
LuaSpecial place(1);
|
|
||||||
LuaSpecial fname(2);
|
|
||||||
|
|
||||||
// If they passed in a single tangible, convert it to a tangible list.
|
// If they passed in a single tangible, convert it to a tangible list.
|
||||||
int64_t place_id = LS.tanid(place);
|
int64_t place_id = LS.tanid(place);
|
||||||
@@ -677,10 +670,10 @@ LuaDefine(tangible_start, "tangible,function,arg1,arg2...",
|
|||||||
// Create a new thread, set up function and arguments.
|
// Create a new thread, set up function and arguments.
|
||||||
lua_State *CO = LS.newthread(thread);
|
lua_State *CO = LS.newthread(thread);
|
||||||
lua_pushvalue(L, func.index());
|
lua_pushvalue(L, func.index());
|
||||||
for (int i = 0; i < varlen; i++) {
|
for (int i = 0; i < extra.size(); i++) {
|
||||||
lua_pushvalue(L, i + 3);
|
lua_pushvalue(L, extra.index(i));
|
||||||
}
|
}
|
||||||
lua_xmove(L, CO, varlen + 1);
|
lua_xmove(L, CO, extra.size() + 1);
|
||||||
|
|
||||||
// Create the thread info table.
|
// Create the thread info table.
|
||||||
LS.newtable(thinfo);
|
LS.newtable(thinfo);
|
||||||
|
|||||||
@@ -310,10 +310,13 @@ int64_t World::connection_create() {
|
|||||||
assert(stack_is_clear());
|
assert(stack_is_clear());
|
||||||
int64_t id = id_global_pool_.get_one();
|
int64_t id = id_global_pool_.get_one();
|
||||||
{
|
{
|
||||||
LuaVar database, classtab, mt, func;
|
LuaVar database, classtab, config, mt, func;
|
||||||
LuaExtStack LS(state(), database, classtab, mt, func);
|
LuaExtStack LS(state(), database, classtab, config, mt, func);
|
||||||
Tangible *tan = tangible_make(LS, database, id);
|
Tangible *tan = tangible_make(LS, database, id);
|
||||||
|
|
||||||
|
// A blank config table to pass to 'init'
|
||||||
|
LS.newtable(config);
|
||||||
|
|
||||||
// Set the login flags.
|
// Set the login flags.
|
||||||
if (is_authoritative()) {
|
if (is_authoritative()) {
|
||||||
tan->client_id_ = id;
|
tan->client_id_ = id;
|
||||||
@@ -327,7 +330,9 @@ int64_t World::connection_create() {
|
|||||||
|
|
||||||
if (is_authoritative()) {
|
if (is_authoritative()) {
|
||||||
LS.rawget(func, classtab, "init");
|
LS.rawget(func, classtab, "init");
|
||||||
spawn(LS, id, id, func, 0, false);
|
lua_pushvalue(state(), database.index());
|
||||||
|
lua_pushvalue(state(), config.index());
|
||||||
|
spawn(LS, id, id, func, 2, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
connections_.emplace(id, id);
|
connections_.emplace(id, id);
|
||||||
@@ -572,71 +577,6 @@ void World::probe_lua_call(int64_t actor_id, int64_t place_id, std::string_view
|
|||||||
clear_lthread_state();
|
clear_lthread_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// This is called from World::update_source, and also
|
|
||||||
// from World::patch_source in the difference transmitter.
|
|
||||||
//
|
|
||||||
// When called from the difference transmitter, we suppress
|
|
||||||
// error messages.
|
|
||||||
//
|
|
||||||
// For the moment, errors are channeled to util::dprint,
|
|
||||||
// and 'print' statements just go to std::cerr. Neither
|
|
||||||
// of these is ideal. We need to get serious about setting
|
|
||||||
// up error handling.
|
|
||||||
//
|
|
||||||
// We also need to figure out a solution for what happens if
|
|
||||||
// some lua source file tries to modify, say, tangible state
|
|
||||||
// in top-level code.
|
|
||||||
//
|
|
||||||
bool World::rebuild_sourcedb(int64_t actor_id) {
|
|
||||||
int successes = 0;
|
|
||||||
int failures = 0;
|
|
||||||
for (const eng::string &mod: source_db_.modules()) {
|
|
||||||
open_lthread_state(0, 0, 0, false);
|
|
||||||
eng::string err = source_db_.rebuild_module(mod);
|
|
||||||
eng::string prints = lthread_prints_.str();
|
|
||||||
clear_lthread_state();
|
|
||||||
if (err.empty()) successes ++;
|
|
||||||
else failures ++;
|
|
||||||
if ((!err.empty()) || (!prints.empty())) {
|
|
||||||
lthread_prints_ << "Compiling " << mod << ":" << std::endl;
|
|
||||||
if (!err.empty()) lthread_prints_ << err << std::endl;
|
|
||||||
if (!prints.empty()) lthread_prints_ << prints;
|
|
||||||
util::dprintview(lthread_prints_.view());
|
|
||||||
if (actor_id != 0) lthread_prints_to_actor(actor_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
source_db_.rebuild_funcnames();
|
|
||||||
lthread_prints_ << "Compiled " << successes << " modules successfully." << std::endl;
|
|
||||||
if (failures > 0) {
|
|
||||||
lthread_prints_ << "Compiled " << failures << " modules with errors." << std::endl;
|
|
||||||
}
|
|
||||||
util::dprintview(lthread_prints_.view());
|
|
||||||
if (actor_id > 0) lthread_prints_to_actor(actor_id);
|
|
||||||
return (failures == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool World::update_source(const util::LuaSourceVec &source, int64_t actor_id) {
|
|
||||||
assert(stack_is_clear());
|
|
||||||
source_db_.update(source);
|
|
||||||
return rebuild_sourcedb(actor_id);
|
|
||||||
assert(stack_is_clear());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool World::update_source(std::string_view sourcepack, int64_t actor_id) {
|
|
||||||
if (sourcepack.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
StreamBuffer sb(sourcepack);
|
|
||||||
util::LuaSourceVec sv;
|
|
||||||
SourceDB::deserialize_source(&sv, &sb);
|
|
||||||
return update_source(sv, actor_id);
|
|
||||||
} catch (const StreamException &ex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void World::http_response(const HttpParser &response) {
|
void World::http_response(const HttpParser &response) {
|
||||||
// Find the request.
|
// Find the request.
|
||||||
auto iter = http_requests_.find(response.request_id());
|
auto iter = http_requests_.find(response.request_id());
|
||||||
@@ -1019,23 +959,41 @@ void World::invoke_lua_source(int64_t actor_id, int64_t place_id, std::string_vi
|
|||||||
if (actor_id != place_id) return;
|
if (actor_id != place_id) return;
|
||||||
|
|
||||||
// Check if this is the first time we're loading the source.
|
// Check if this is the first time we're loading the source.
|
||||||
bool brand_new = (source_db_.modules().size() == 1);
|
bool brand_new = (source_db_.modules().size() == 0);
|
||||||
|
|
||||||
// Compile and load the source.
|
// Deserialize the datapack.
|
||||||
bool success = update_source(datapack, actor_id);
|
util::LuaSourceVec sv = SourceDB::deserialize_source(datapack);
|
||||||
|
if (sv.empty()) return;
|
||||||
|
|
||||||
|
// Try to compile the code.
|
||||||
|
open_lthread_state(actor_id, actor_id, 0, false);
|
||||||
|
eng::string errors = source_db_.update(sv);
|
||||||
|
|
||||||
|
if (errors.empty()) {
|
||||||
|
lthread_prints_ << "Compiled source successfully.\n";
|
||||||
|
} else {
|
||||||
|
lthread_prints_ << "Compiling source: \n" << errors;
|
||||||
|
}
|
||||||
|
util::dprint(lthread_prints_.view());
|
||||||
|
lthread_prints_to_actor(actor_id);
|
||||||
|
|
||||||
// Call world.init
|
// Call world.init
|
||||||
if (brand_new) {
|
if (brand_new) {
|
||||||
if (success) {
|
if (errors.empty()) {
|
||||||
{
|
{
|
||||||
lua_State *L = state();
|
lua_State *L = state();
|
||||||
LuaVar lclass, lfunc;
|
LuaVar lclass, lfunc, tangibles, actor, config;
|
||||||
LuaExtStack LS(L, lclass, lfunc);
|
LuaExtStack LS(L, lclass, lfunc, tangibles, actor, config);
|
||||||
|
LS.rawget(tangibles, LuaRegistry, "tangibles");
|
||||||
|
LS.rawget(actor, tangibles, actor_id);
|
||||||
|
LS.newtable(config);
|
||||||
|
|
||||||
LS.getclass(lclass, "world");
|
LS.getclass(lclass, "world");
|
||||||
if (LS.classname(lclass) != "") {
|
if (LS.classname(lclass) != "") {
|
||||||
LS.rawget(lfunc, lclass, "init");
|
LS.rawget(lfunc, lclass, "init");
|
||||||
spawn(LS, actor_id, place_id, lfunc, 0, false);
|
lua_pushvalue(L, actor.index());
|
||||||
|
lua_pushvalue(L, config.index());
|
||||||
|
spawn(LS, actor_id, place_id, lfunc, 2, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
run_scheduled_threads();
|
run_scheduled_threads();
|
||||||
|
|||||||
@@ -290,11 +290,7 @@ void World::diff_tanclass(int64_t actor_id, World *master, StreamBuffer *xsb) {
|
|||||||
|
|
||||||
void World::patch_source(StreamBuffer *sb, DebugCollector *dbc) {
|
void World::patch_source(StreamBuffer *sb, DebugCollector *dbc) {
|
||||||
DebugBlock dbb(dbc, "patch_source");
|
DebugBlock dbb(dbc, "patch_source");
|
||||||
bool modified = source_db_.patch(sb, dbc);
|
source_db_.patch(sb, dbc);
|
||||||
if (modified) {
|
|
||||||
rebuild_sourcedb(0);
|
|
||||||
DebugLine(dbc) << "Source DB rebuilt";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::diff_source(World *master, StreamBuffer *sb) {
|
void World::diff_source(World *master, StreamBuffer *sb) {
|
||||||
@@ -359,6 +355,7 @@ void World::diff_globals(World *master, StreamBuffer *sb) {
|
|||||||
|
|
||||||
int64_t World::patch(StreamBuffer *sb, DebugCollector *dbc) {
|
int64_t World::patch(StreamBuffer *sb, DebugCollector *dbc) {
|
||||||
DebugBlock dbb(dbc, "patch");
|
DebugBlock dbb(dbc, "patch");
|
||||||
|
clear_lthread_state();
|
||||||
int64_t actor_id = patch_actor(sb, dbc);
|
int64_t actor_id = patch_actor(sb, dbc);
|
||||||
patch_visible(sb, dbc);
|
patch_visible(sb, dbc);
|
||||||
bool full = sb->read_bool();
|
bool full = sb->read_bool();
|
||||||
|
|||||||
@@ -242,23 +242,6 @@ public:
|
|||||||
//
|
//
|
||||||
SourceDB &get_source() { return source_db_; }
|
SourceDB &get_source() { return source_db_; }
|
||||||
|
|
||||||
// Rebuild the global environment from the source database.
|
|
||||||
//
|
|
||||||
// Error messages go to the specified actor, and also dprint.
|
|
||||||
//
|
|
||||||
// Returns true if the rebuild goes without errors.
|
|
||||||
//
|
|
||||||
bool rebuild_sourcedb(int64_t actor_id);
|
|
||||||
|
|
||||||
// Update the source database from disk, then rebuild the global environment.
|
|
||||||
//
|
|
||||||
// Error messages go to the specified actor, and also dprint.
|
|
||||||
//
|
|
||||||
// Returns true if the update goes without errors.
|
|
||||||
//
|
|
||||||
bool update_source(const util::LuaSourceVec &source, int64_t actor_id);
|
|
||||||
bool update_source(std::string_view sourcepk, int64_t actor_id);
|
|
||||||
|
|
||||||
// Supply an HTTP response to an outstanding HTTP request.
|
// Supply an HTTP response to an outstanding HTTP request.
|
||||||
//
|
//
|
||||||
void http_response(const HttpParser &response);
|
void http_response(const HttpParser &response);
|
||||||
|
|||||||
@@ -4,24 +4,94 @@ makeclass('probe')
|
|||||||
makeclass('invoke')
|
makeclass('invoke')
|
||||||
makeclass('cube')
|
makeclass('cube')
|
||||||
makeclass('sphere')
|
makeclass('sphere')
|
||||||
|
makeclass('pylon')
|
||||||
|
makeclass('roster')
|
||||||
|
|
||||||
|
function pretty(t, indent, seen)
|
||||||
|
indent = indent or 0
|
||||||
|
seen = seen or {}
|
||||||
|
|
||||||
|
if type(t) ~= "table" then return tostring(t) end
|
||||||
|
if seen[t] then return "<cycle>" end
|
||||||
|
seen[t] = true
|
||||||
|
|
||||||
|
local spacing = string.rep(" ", indent)
|
||||||
|
local out = "{\n"
|
||||||
|
|
||||||
|
for k, v in pairs(t) do
|
||||||
|
out = out .. spacing .. " [" .. tostring(k) .. "] = "
|
||||||
|
out = out .. pretty(v, indent + 1, seen) .. ",\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
return out .. spacing .. "}"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- tangible.redirect(actor1,actor2)
|
||||||
|
-- tangible.forcedisconnect(actor)
|
||||||
|
-- tangible.keepactor(actor) -- Do not autodelete upon disconnect
|
||||||
|
-- tangible.delete(actor) -- If actor is a connected player it will disconnect
|
||||||
|
|
||||||
-- This gets called on every login except the admin user.
|
-- This gets called on every login except the admin user.
|
||||||
function login.init()
|
function login.init(self, config)
|
||||||
local player = global.get("nextplayer")
|
local player = global.get("nextplayer")
|
||||||
global.set("nextplayer", player + 1)
|
global.set("nextplayer", player + 1)
|
||||||
dprint("login.init initializing player ", player)
|
printf("login.init initializing %p, player %d", self, player)
|
||||||
actor.player = player
|
self.player = player
|
||||||
tangible.keepactor(actor) -- do not delete this login when the client disconnects
|
self.color={0,0,0,0,0,0,0,0,0,0,0,0}
|
||||||
tangible.animinit{tan=actor, anim={bp="character", mesh="manny", plane="earth", xyz={player * 100, 0, 90}}}
|
self.kills=0
|
||||||
|
self.killed=0
|
||||||
|
tangible.animinit{tan=self, anim={bp="character", mesh="manny", plane="earth", xyz={player * 100, 0, 90}}}
|
||||||
|
tangible.start(self, login.startscanning)
|
||||||
|
end
|
||||||
|
|
||||||
|
function login.startscanning()
|
||||||
|
printf("Scanning started. Actor=%p Place=%p", actor, place)
|
||||||
|
while true do
|
||||||
|
login.onescan()
|
||||||
|
wait(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function login.onescan()
|
||||||
|
local nearby=tangible.find{plane=tangible.animfinal(actor).plane,center=tangible.animfinal(actor).xyz,radius=1000,shape="cylinder"}
|
||||||
|
for k,v in pairs(nearby) do
|
||||||
|
if classname(v)=="pylon" then actor.color[v.color]=time()+30 end
|
||||||
|
if classname(v)=="player" then
|
||||||
|
if actor.team==v.team then
|
||||||
|
for ck,cv in v.color do actor.color[ck]=math.max(actor.color[ck],cv) end
|
||||||
|
else
|
||||||
|
local score1=0
|
||||||
|
local score2=0
|
||||||
|
for ck,cv in actor.color do if time()-cv<0 then score1=score1+1 end end
|
||||||
|
for ck,cv in v.color do if time()-cv<0 then score2=score2+1 end end
|
||||||
|
if score1-score2>0 then
|
||||||
|
|
||||||
|
-- kill player 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- This gets called on the admin user. You can call login.init in here if you want.
|
-- This gets called on the admin user. You can call login.init in here if you want.
|
||||||
function world.init()
|
function world.init(self, config)
|
||||||
dprint("world.init")
|
print("world.init")
|
||||||
global.set("nextplayer", 0)
|
global.set("nextplayer", 0)
|
||||||
|
tangible.build{class=roster, anim={plane="earth", xyz={2000,0,0} } }
|
||||||
tangible.build{class=cube, anim={plane="earth", xyz={500,-100,0}, mat_color={1,0,0}}}
|
tangible.build{class=cube, anim={plane="earth", xyz={500,-100,0}, mat_color={1,0,0}}}
|
||||||
tangible.build{class=sphere, anim={plane="earth", xyz={500,100,0}, mat_color={0,0,1}}}
|
tangible.build{class=sphere, anim={plane="earth", xyz={500,100,0}, mat_color={0,0,1}}}
|
||||||
login.init()
|
login.init(self, config)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function roster.init(self,config)
|
||||||
|
self.character={}
|
||||||
|
end
|
||||||
|
|
||||||
|
function world.buildpylon()
|
||||||
|
print("Building pylon")
|
||||||
|
local radius=1000
|
||||||
|
tangible.build{class=pylon,plane="earth",xyz={math.random(-radius,radius),math.random(-radius,radius),0}}.color=math.random(1,12)
|
||||||
end
|
end
|
||||||
|
|
||||||
function invoke.move(action, xyz, facing)
|
function invoke.move(action, xyz, facing)
|
||||||
@@ -38,20 +108,60 @@ function login.lookmenu(add)
|
|||||||
add("Redirect", function() tangible.redirect(actor, place) end)
|
add("Redirect", function() tangible.redirect(actor, place) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function cube.lookmenu(add)
|
-- Four cases: Unnamed to new, Unnamed to existing, Named to new, Named to Existing
|
||||||
add("Cube A", function () dprint("Doing Cube A") end)
|
function playas(who)
|
||||||
add("Cube B", function () dprint("Doing Cube B") end)
|
printf("Playas %lP",place)
|
||||||
add("Cube C", function () dprint("Doing Cube C") end)
|
if not actor.name and not place.character[who] then -- Unnamed to new
|
||||||
add("Cube Hi", function () dprint("Doing Cube Hi") end)
|
printf("Case 1: Naming this character "..who)
|
||||||
add("Cube Bye", function () dprint("Doing Cube Bye") end)
|
actor.name=who
|
||||||
add("Cube Yo", function () dprint("Doing Cube Yo") end)
|
tangible.keepactor(actor)
|
||||||
add("Cube Z", function () dprint("Doing Cube Z") end)
|
place.character[who]=actor
|
||||||
|
elseif actor.name and not place.character[who] then -- Named to new (Hard one)
|
||||||
|
printf("Case 2: Creating new character "..who)
|
||||||
|
printf("Case 2.0")
|
||||||
|
local nc=tangible.build{class=login, anim={plane="earth", xyz={0,0,0} } }
|
||||||
|
printf("Case 2.1")
|
||||||
|
nc.name=who
|
||||||
|
printf("Case 2.2")
|
||||||
|
tangible.keepactor(nc)
|
||||||
|
printf("Case 2.3")
|
||||||
|
place.character[who]=nc
|
||||||
|
printf("Case 2.4: ...Place is now "..place)
|
||||||
|
tangible.redirect(actor,nc)
|
||||||
|
elseif not actor.name and place.character[who] then
|
||||||
|
printf("Case 3: Logging in from unnamed to existing character "..who)
|
||||||
|
tangible.redirect(actor,place.character[who])
|
||||||
|
elseif actor.name and place.character[who] then
|
||||||
|
printf("Case 4: Logging in from "..actor.name.." to existing character "..who)
|
||||||
|
tangible.redirect(actor,place.character[who])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function roster.lookmenu(add)
|
||||||
|
for _,name in ipairs { "Albert", "Betty", "Cornelius" } do
|
||||||
|
add("Play as "..name..(place.character[name] and "" or "*"), function() playas(name) end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function cube.lookmenu(add)
|
||||||
|
add("Cube A", function () print("Doing Cube A") end)
|
||||||
|
add("Cube B", function () print("Doing Cube B") end)
|
||||||
|
add("Cube C", function () print("Doing Cube C") end)
|
||||||
|
add("Cube Hi", function () print("Doing Cube Hi") end)
|
||||||
|
add("Cube Bye", function () print("Doing Cube Bye") end)
|
||||||
|
add("Cube Yo", function () print("Doing Cube Yo") end)
|
||||||
|
add("Cube Z", function () print("Doing Cube Z") end)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function sphere.lookhotkeys(add)
|
function sphere.lookhotkeys(add)
|
||||||
add("FaceL", "Sphere Hi", function () dprint("Doing Sphere Hi") end)
|
add("FaceL", "Sphere Hi", function () print("Doing Sphere Hi") end)
|
||||||
add("FaceM", "Sphere Bye", function () dprint("Doing Sphere Bye") end)
|
add("FaceM", "Sphere Bye", function () print("Doing Sphere Bye") end)
|
||||||
add("FaceR", "Sphere Yo", function () dprint("Doing Sphere Yo") end)
|
add("FaceR", "Sphere Yo", function () print("Doing Sphere Yo") end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function sphere.tick(foo)
|
||||||
|
print("Tick")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user