Jump to content

Plugins in Leadwerks Game Engine 5

Josh

2,190 views

Internally, Leadwerks Editor uses an EventHandler class for every interface in the program. The material editor is a class extended from the EventHandler. So is the little window that has all the controls to calculate normals. So is every viewport.

The event handler class has one important function:

Event ProcessEvent(Event)

Every EventHandler has access to events as they occur. This is how all program actions are handled in the editor.

The plugin system will work by hooking into the event system. Each plugin will have a Lua script that receive events before the rest of the program sees them:

function Script:ProcessEvent(event)
	return event
end

If the plugin makes no changes to the event then it simply returns the original event. The returned event is then sent to other event handlers.

Here is an example of a plugin that would disable the close window button on the main window. Because the function returns nil the event is discarded before the main window ever evaluates it:

function Script:ProcessEvent(event)
	if event.id == EVENT_WINDOWCLOSE and event.source == editor.mainwindow then
		return nil
	else
		return event
	end
end

Here is an example of a very mean plugin that would make it so that clicking the File > Open menu item in the main window quits the program:

function Script:ProcessEvent(event)
	if event.id == EVENT_MENUEVENT then
		if event.source == editor.mainwindow then
			if event.extra == MENU_FILEOPEN then
				event.id = EVENT_WINDOWCLOSE
			end
		end
	end
  	return event
end

Okay, now let's see if we can design a plugin for something people would actually want. Let's imagine we have a new visual material design system. The exact details of how it works are not important, it's just a system that overrides the default material editor. The design system would require materials to have a special file associated with them with the extension .DESIGN. If you open the material "brick.mat" we will look for a file in the same folder called "brick.design". If the design file is found we open the material in our special editor. If the design file is missing we will just fall back to the default material editor.

Now let's see how our system can handle this:

function Script:Start()
	
	--Create our interface
	self.window = CreateWindow("Material Designer",0,0,800,600,editor.mainwindow,WINDOW_CENTER + WINDOW_TITLEBAR + WINDOW_RESIZABLE)
	
end

function Script:ProcessEvent(event)
	if event.id == EVENT_FILEOPEN

		--Check for material files being opened
		if ExtractExt(event.extra)=="mat"
      
			--Look for design file
			local designfilename = StripExt(event.extra).".design"
			if FileType( designfilename ) == 1 then
				
				--Load the design file
				local stream = ReadFile(designfilename)
				if stream ~= nil then
					
					--Display our custom material editor
					self.window:Show()
					self.window:Activate()

				else
          
					Print("Error: Failed to load design file.")
            
				end
				
				--Discard the event
				return nil
			end
		end
	end
	return event
end

As you can see, this approach is extremely powerful. The event IDs and design rarely change, if ever, so this allows a lot of flexibility and at the same time gives us the optimal compatibility as changes are made to the core editor. With this approach to plugins you can literally do anything you want in the editor.

  • Like 4
  • Thanks 1
  • Upvote 1


56 Comments


Recommended Comments



Nice Feature Josh. This will going to open alot of possibilities to creater new editor features... 

Share this comment


Link to comment

This would not have been possible in the Leadwerks 4 editor. Leadwerks 5 uses C++ for the editor and engine, which is really possible because of our use of C++11 shared pointers, which makes complex data management a lot easier. We can also expose a new API of internal editor mechanics to Lua now.

Share this comment


Link to comment

Will this allow us to draw 3D elements on the 3rd view? Or save data to an entity in the GetKeyValue/SetKeyValue functions?

It would be nice to automatically re-skin NPCs in my game based upon my backend game data.

Share this comment


Link to comment
9 minutes ago, martyj said:

Will this allow us to draw 3D elements on the 3rd view? Or save data to an entity in the GetKeyValue/SetKeyValue functions?

It would be nice to automatically re-skin NPCs in my game based upon my backend game data.

You could create a new entity in a script that gets rendered in the 3D world, yes.

I don't know about map format customization yet but that does not sound too hard. I would want to implement it with key values like you describe, or something like that, so that the maps can be loaded by any Leadwerks program without any special code or plugin required.

You could even distribute a set of plugins with your game to modify the editor into your own special game editor.

  • Like 1

Share this comment


Link to comment
function Script:Start()
  editor.materialeditor.exportextensions = editor.materialeditor.exportextensions + "Truevision Targa (*.tga):tga;"
end

function Script:ProcessEvent( event )
  if event.id == EVENT_FILEEXPORT then
    if event.source == editor.textureeditor then
      if ExtractExt( event.extra ) == "tga" then
        local stream = WriteFile( event.extra )
        --write file data here
        stream.Close()
        return nil
      end
    end
  end
  return event
end

Theoretical example to add a TGA exporter to the texture editor.

Share this comment


Link to comment
On 3/10/2018 at 5:02 PM, Josh said:

I don't know about map format customization yet but that does not sound too hard. 

Would that also allow us to add items to the scene tree programmatically?

Share this comment


Link to comment
2 hours ago, AggrorJorn said:

Would that also allow us to add items to the scene tree programmatically?

Yes. Not every entity is shown in the scene tree. You will create a SceneObject from an entity and that will be editable and show up in the tree.

this is going to be quite fun. 😁

  • Like 2

Share this comment


Link to comment

The examples seem to start with a file extension launching custom dialogs. Will there be a way to add menu items to launch dialogs?

Share this comment


Link to comment

That would actually be really great to beable to draw widgets on the editor... maybe leave a little blank space for em :) Although I feel like this can be done regardless so long as the Editor uses LE's UI/Window system. Or if the widget sidebar was a scrollable panel, and you could add elements to it?

Share this comment


Link to comment
15 hours ago, Rick said:

The examples seem to start with a file extension launching custom dialogs. Will there be a way to add menu items to launch dialogs?

This example shows how you might add a menu item to the main window menu and use it to open a custom dialog:

function Script:Start()
  local menu = editor.mainmenu:FindChild("Tools",false)
  self.menu_open = CreateMenuItem("Material Designer",menu)
  self.window = CreateWindow("Material Designer",0,0,800,600,editor.mainwindow)
end

function Script:ProcessEvent(event)
  if event.id == EVENT_MENUACTION and event.source == self.menu_open then
    self.window:Show()
    self.window:Activate()
    return nil
  else
    return event
  end
end

I really like this approach because it is almost like open-sourcing the editor but even better because all the customizations are plug and play.

  • Like 1

Share this comment


Link to comment

Ahhhh custom menu items :wub:. I have been looking forward to this for so many years. Can't wait to get my hand on this.

Share this comment


Link to comment

I'm going to shoot pie in the sky here since this seems like it's in the early stages. The editor, if I recall, uses ENET. Now to create these add-ons using the LE UI or our own custom built will be painful. Having limited controls available and having to type out the creation/positioning of any kind of complex UI is really unreasonable in 2018. So I think about why not let users use other tools to create these UI add-ons like .NET or Java or anything else they like (BMAX?). I think about the communication challenges with doing this. After all these add-ons would have to talk to the main LE editor to manipulate things and get information. So how about some kind of ENET communication system? Use TCP and create/document commands and their parameters for our UI add-ons to manipulate and communicate with the LE editor. This would really open the door to easier add-on creation and would create a nice API layer for the editor itself. Something that I'm sure will have unforeseen benefits down the line.

  • Like 1

Share this comment


Link to comment
14 minutes ago, Rick said:

I'm going to shoot pie in the sky here since this seems like it's in the early stages. The editor, if I recall, uses ENET. Now to create these add-ons using the LE UI or our own custom built will be painful. Having limited controls available and having to type out the creation/positioning of any kind of complex UI is really unreasonable in 2018. So I think about why not let users use other tools to create these UI add-ons like .NET or Java or anything else they like (BMAX?). I think about the communication challenges with doing this. After all these add-ons would have to talk to the main LE editor to manipulate things and get information. So how about some kind of ENET communication system? Use TCP and create/document commands and their parameters for our UI add-ons to manipulate and communicate with the LE editor. This would really open the door to easier add-on creation and would create a nice API layer for the editor itself. Something that I'm sure will have unforeseen benefits down the line.

Maybe it would make sense to build the editor into the engine and make it part of the API even? I have considered this.

The final editor might just be a Lua script file that creates and positions various components that are built into the engine.

Another thing to consider is someone might make a plugin that lets you visually create UI components.

Share this comment


Link to comment
1 hour ago, Josh said:

Maybe it would make sense to build the editor into the engine and make it part of the API even? I have considered this.

This is a possibility. Some engines do this. I don't personally like it because I believe with those engines you run the editor from Visual Studio which sort of kills the ease of use for newbies.

 

1 hour ago, Josh said:

Another thing to consider is someone might make a plugin that lets you visually create UI components.

While this is for sure possible and I would love it, in my view UI elements for a game vs an editor of sorts are 2 completely different things and very hard to pull off to making them one. The editor you have and any add-on editor someone would create is simply easier, more efficient and more standard (more people will know it) to do with something like .NET or Java or even BMax like you did (although less ppl know that compared to the other 2 mentioned). However, like I was saying as long as there is a networked way to communicate with the LE editor then people can use whatever they want and feel comfortable with because all of those frameworks can make network connections to do that communication very easily.

Share this comment


Link to comment
4 minutes ago, Rick said:

This is a possibility. Some engines do this. I don't personally like it because I believe with those engines you run the editor from Visual Studio which sort of kills the ease of use for newbies.

 

While this is for sure possible and I would love it, in my view UI elements for a game vs an editor of sorts are 2 completely different things and very hard to pull off to making them one. The editor you have and any add-on editor someone would create is simply easier, more efficient and more standard (more people will know it) to do with something like .NET or Java or even BMax like you did (although less ppl know that compared to the other 2 mentioned). However, like I was saying as long as there is a networked way to communicate with the LE editor then people can use whatever they want and feel comfortable with because all of those frameworks can make network connections to do that communication very easily.

This actually could be accomplished with a Lua plugin that uses the network API to communicate.

Share this comment


Link to comment
31 minutes ago, Rick said:

 However, like I was saying as long as there is a networked way to communicate with the LE editor then people can use whatever they want and feel comfortable with because all of those frameworks can make network connections to do that communication very easily.

That would also go for the debugger. If we had an api to Leadwerk we can add our own IDE's. I use Visual studio code for writing Lua but it lacks the debugging options. It is possible to extend the tool with a custom debugger but not without having access to leadwerks debugger functionality. 

Share this comment


Link to comment
1 hour ago, AggrorJorn said:

That would also go for the debugger. If we had an api to Leadwerk we can add our own IDE's. I use Visual studio code for writing Lua but it lacks the debugging options. It is possible to extend the tool with a custom debugger but not without having access to leadwerks debugger functionality. 

I have VS Code but haven't worked too much with it. It's crossplatform too isn't it? Do you like it? Honestly with it being newer and more lightweight I'm sure it's great and could probably be considered as a defacto editor for lua and LE.

Share this comment


Link to comment
2 hours ago, Rick said:

I have VS Code but haven't worked too much with it. It's crossplatform too isn't it? Do you like it? Honestly with it being newer and more lightweight I'm sure it's great and could probably be considered as a defacto editor for lua and LE.

Some advantages that make it my default text editor next to notepad++

  • Comes with various default styles, but can be completely styled to ones desire.
  • Supports many languages. Lua support via plugins.
  • Lightweight 
  • Crossplatform
  • You can attach your own debugger code via debugger extensions. 
  • My favorite: can be easily extended with custom plugins.
    • I could make this little leadwerks plugin that looks at the online documentation and shows it in the second screen. It could actually load in the entire Leadwerks api from here and display it for you.
    • You can even extend the intellisense using your own documentation
    • Something like this: https://github.com/Microsoft/vscode-extension-samples/tree/master/contentprovider-sample
    • it can automatically install a lua intellisense plugin as a dependency.
    • You can import all keyboard shortcuts from visual studio if you want
    • I ahve got dozens of little snippets already made this way: 
      • For instance type: lestart followed by 2 tabs and you get:
function Script:Start()

end

 

 

  • Like 1

Share this comment


Link to comment

How do you make intellisense work with lua?

Share this comment


Link to comment

Can it be configured to display Leadwerks commands? How does it know the object type? For example, in this function how can you tell it that the entity is a Leadwerks entity object?

function Script:Collision(entity0,entity1,position,normal,speed)
	entity0:...
end

 

Share this comment


Link to comment
7 hours ago, Josh said:

Here is the source to the script debugger:

I will do some experimenting with this. 

Share this comment


Link to comment
18 hours ago, Josh said:

Can it be configured to display Leadwerks commands? How does it know the object type? For example, in this function how can you tell it that the entity is a Leadwerks entity object?


function Script:Collision(entity0,entity1,position,normal,speed)
	entity0:...
end

 

These are things that I haven't tried yet. I will have to play around with this before I can give an accurate answer. Like I said, it is not as powerful as with strong typed languages like C#/C++. But maybe something like showing a list of all Leadwerks functions is possible once you have type something like "myVariable:". The 'intellisense' you saw in the screenshot above only roughly displays some of the functions it can find on the page and perhaps some other methods. Either way a custom intellisense extension could work/help, but will not be as sophisticated as C#/C++.

Perhaps when the colon is typed, the variable used in this case, could be lookup in the current page and see where it is first declared. The intellisense could see that 'Model:load' is used and 'assumes' it is a model, display Leadwerks model functions first. I don't know, just thinking out loud here. 

local someVariable = Model:Load()
someVariable: 

There is enough stuff out here to spend a lot of time on. Will make a video/blog if I have something worthwhile.

Share this comment


Link to comment

It can't tell that entity0 or entity1 are entities. Nothing can. One of the downfalls to typeless languages is the tooling can't help. This is why languages like TypeScript exist and sit on top of Javascript. For the tooling, because when you get into bigger projects it's helpful to have tools help you code and find bugs.

Lua seems to have similar project to do the same thing: https://github.com/andremm/typedlua but it's dead and obviously wouldn't recommend it anyway as the flexibility of not being a typed language has it's benefits, especially in game dev. However, some form of autocomplete, as you'e done, is always possible it's just not what people have come to know as autocomplete in other typed languages. Since autocomplete will be an issue for every editor, it's the other things to look at for an editor.

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Create Your Account

Sign in

Already have an account? Sign in here.

Sign In Now
×