http://opensimulator.org/api.php?action=feedcontributions&user=Aiaustin&feedformat=atomOpenSimulator - User contributions [en]2024-03-29T15:43:01ZUser contributionsMediaWiki 1.19.9http://opensimulator.org/wiki/0.9.3.00.9.3.02024-01-23T10:19:56Z<p>Aiaustin: /* General */ 0.9.3.0 dev source code now back on GitHub master source code</p>
<hr />
<div>{{ReleaseInfo}}<br />
{{Quicklinks|0.9.3.0}}<br />
<br />
= General =<br />
''' VERSION UNDER DEVELOPMENT'''<br />
<br />
Also see [http://opensimulator.org/wiki/0.9.2.2_Release 0.9.2.2 Release Notes]<br />
<br />
= Breaking Changes =<br />
* XEngine is no longer supported. Some scripts with peculiar syntax (or things like divisions by zero, abusive implicit casts, etc), will need fix.<br />
* old ODE is no longer supported. Old objects, in particular lsl vehicles, may not work under Bullet or ubODE.<br />
* LSL function llList2ListStrided changed to be more compatible with spec. Old one added back with name osOldList2ListStrided<br />
* LSL function llListFindList changed to be more compatible with spec. Some older scripts may not work as before<br />
<br />
= Known Issues =<br />
<br />
= Requirements =<br />
<br />
OpenSimulator 0.9.3.0 requires:<br />
<br />
* [https://dotnet.microsoft.com/en-us/download/dotnet/6.0 dotnet6] runtime for your platform (also the SDK if you wish to compile) <br />
* On Windows you may need to install the [https://docs.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170 run time files for vc++]<br />
* On windows you may need to authorize the install of older .NetFramework 3.5<br />
* on linux you will need libgdiplus<br />
**if you have mono 6.x complete, you already have libgdiplus, otherwise you need to install it for example on debian:<br />
**apt-get update && apt-get install -y apt-utils libgdiplus libc6-dev<br />
<br />
<br />
Due to database migration renumbering which occurred at release 0.9.0.0, if you are upgrading from a version of OpenSimulator prior to 0.8.2.1, then you MUST first upgrade to *0.8.2.1* and then proceed to upgrade directly to 0.9.3.0. See [[0.9.0.0_Release#Pivot_Release:_0.8.2.1]] for more advice.<br />
<br />
= Changes and Fixes =<br />
* New OSSL functions: [[osGetParcelID]], [[osGetParcelIDs]], [[osGetParcelDetails]], [[osListSortInPlaceStrided]], [[osGetPrimCount]] and [[osGetSittingAvatarsCount]]<br />
* New LSL functions: llLinear2sRGB, llsRGB2Linear, llListSortStrided, llList2ListSlice and llListFindStrided<br />
* LSL function llList2ListStrided changed to be more compatible with spec. Old one added back with name osOldList2ListStrided<br />
<br />
= Acknowledgements =<br />
<br />
Many thanks to all the developers (and their cats), testers and community members who contributed to this release and who help out with OpenSimulator generally. Your hard work makes this all possible.<br />
<br />
[[Category:Release Notes]]</div>Aiaustinhttp://opensimulator.org/wiki/Template:FrontPageImageTemplate:FrontPageImage2023-05-01T11:10:23Z<p>Aiaustin: Widen Front Page Sidebar to accommodate Binaries 0.9.x.x text</p>
<hr />
<div><tr valign="top" style="background: #EEF3E2"><td style="width: 156px; white-space: nowrap; padding: 4px 1em 0 0.5em; border-bottom: 1px solid #aaaaaa;">'''Image of past Moment:'''</td></tr> <tr valign="top" style="background: #EEF3E2"><td style="padding: 0px 0 0; background: #F6F9ED; align: center">[[Image:Oscc13_001_002_grady_booch.png|299px|Opensimulator Community Conference 2013]]</td></tr></div>Aiaustinhttp://opensimulator.org/wiki/0.9.2.20.9.2.22023-03-30T12:18:40Z<p>Aiaustin: /* Requirements */ Correct steps of upgrade if from prior to 0.8.2.1</p>
<hr />
<div>{{ReleaseInfo}}<br />
{{Quicklinks|0.9.2.2}}<br />
<br />
= General =<br />
Also see [http://opensimulator.org/wiki/0.9.2.1_Release 0.9.2.1 Release Notes]<br />
<br />
= Known Issues =<br />
* llEmail still only sends to prims on same region or regions on same opensimulator instance.<br />
<br />
= Requirements =<br />
<br />
OpenSimulator 0.9.2.2 requires:<br />
<br />
* At least .NET Framework 4.6 when running under Windows.<br />
* At least Mono 5.x when running under Mono (Linux or Mac).<br />
* On Windows you need to install the [https://docs.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170 run time files for vc++]<br />
* On windows you may need to authorize the install of older .NetFramework 3.5<br />
<br />
Due to database migration renumbering which occurred at release 0.9.0.0, if you are upgrading from a version of OpenSimulator prior to 0.8.2.1, then you MUST first upgrade to *0.8.2.1* and then proceed to upgrade directly to 0.9.2.2 See [[0.9.0.0_Release#Pivot_Release:_0.8.2.1]] for more advice.<br />
<br />
= Changes and Fixes =<br />
* Various small changes to reduce cpu load<br />
* Several changes to llEmail support. SMTP secure connections (finally), several throttles like by number of emails via SMTP or from same owner, to same prim address or to same smtp address<br />
* New script functions llSHA256String; llGetObjectLinkKey, llGetInventoryAcquireTime, ...<br />
* Inventory library capabilities needed for newer viewers.<br />
* support parcel flag "restrict MOAP to this parcel"<br />
<br />
= Acknowledgements =<br />
<br />
Many thanks to all the developers (and their cats), testers and community members who contributed to this release and who help out with OpenSimulator generally. Your hard work makes this all possible.<br />
<br />
[[Category:Release Notes]]</div>Aiaustinhttp://opensimulator.org/wiki/LLUDP_DissectorLLUDP Dissector2023-01-14T17:51:31Z<p>Aiaustin: /* on Linux */</p>
<hr />
<div>__NOTOC__<br />
{{Quicklinks}}<br />
<br /><br />
<br />
== LLUDP protocol dissector ==<br />
This page describes the Wireshark LLUDP Protocol Dissector that can parse the message_template.msg file and use that information to decode all the message fields from the Linden Lab UDP protocol. Up to date code is available on [https://github.com/Neopallium/lludp_dissector GitHub]<br />
<br />
== Installing ==<br />
* Requires Wireshark with Lua support. This is enabled by default in recent versions of Wireshark. [http://wiki.wireshark.org/Lua See this page for Wireshark support for Lua]<br />
<br />
=== on Linux ===<br />
* '''Check if correct''' - Copy all five source files into ~/.wireshark<br />
* '''Check if correct''' - If you need to run Wireshark as the root user or using sudo then you will need to edit the scripts into one file by replacing the dofile("script.lua") calls with the contents of file between the quotes.<br />
* '''Check if correct''' - The other method is to add your user account to the correct group (on Gentoo it is group "wireshark") that will allow your non-root user to capture packets.<br />
<br />
=== on Windows ===<br />
* Copy the lludp folder containing the five .lua source files into the Wireshark AppData directory <br />
<br />
'''Windows versions up to Windows 11'''<br />
<br />
C:\Users\<username>\AppData\Roaming\Wireshark\Plugins <br />
<br />
'''XP/2000'''<br />
<br />
C:\users\<username>\AppData\Wireshark<br />
<br />
* init.lua is not required as LUA support is enabled by default in recent versions of Wireshark.<br />
<br />
== LLUDP preferences ==<br />
There are three preferences that can be changed from Wireshark's Preferences - Protocols - LLUDP dialog:<br />
* Message template file: Full path to the message_template.msg file used to decode message name & details from the packets. On Windows use double backslash '\\' instead of single blackslash '\' to separate directories (Example "C:\\Program Files\\FirestormOS-Releasex64\\app_settings\\message_template.msg").<br />
* UDP port range start: First UDP port to mark as LLUDP packets. (default 13000)<br />
* UDP port range end: Last UDP port to mark as LLUDP packets. (default 13050)<br />
<br />
If your OpenSimulator regions are using only ports 9000-9050 then change the UDP port range.<br />
<br />
=== Code license ===<br />
The Wireshark LLUDP Dissector maybe used under the terms of the "Simplified BSD License" or the [https://github.com/Neopallium/lludp_dissector/blob/master/LICENSE_GPL.txt GPL].</div>Aiaustinhttp://opensimulator.org/wiki/LLUDP_DissectorLLUDP Dissector2023-01-14T17:49:25Z<p>Aiaustin: /* Description of source files */ remove incorrect out of datwe description of the source files. Point to the up to date GitHub version instead.</p>
<hr />
<div>__NOTOC__<br />
{{Quicklinks}}<br />
<br /><br />
<br />
== LLUDP protocol dissector ==<br />
This page describes the Wireshark LLUDP Protocol Dissector that can parse the message_template.msg file and use that information to decode all the message fields from the Linden Lab UDP protocol. Up to date code is available on [https://github.com/Neopallium/lludp_dissector GitHub]<br />
<br />
== Installing ==<br />
* Requires Wireshark with Lua support. This is enabled by default in recent versions of Wireshark. [http://wiki.wireshark.org/Lua See this page for Wireshark support for Lua]<br />
<br />
=== on Linux ===<br />
* Copy all five source files into ~/.wireshark<br />
* '''Check if correct''' If you need to run Wireshark as the root user or using sudo then you will need to edit the scripts into one file by replacing the dofile("script.lua") calls with the contents of file between the quotes.<br />
* '''Check if correct''' The other method is to add your user account to the correct group (on Gentoo it is group "wireshark") that will allow your non-root user to capture packets.<br />
<br />
=== on Windows ===<br />
* Copy the lludp folder containing the five .lua source files into the Wireshark AppData directory <br />
<br />
'''Windows versions up to Windows 11'''<br />
<br />
C:\Users\<username>\AppData\Roaming\Wireshark\Plugins <br />
<br />
'''XP/2000'''<br />
<br />
C:\users\<username>\AppData\Wireshark<br />
<br />
* init.lua is not required as LUA support is enabled by default in recent versions of Wireshark.<br />
<br />
== LLUDP preferences ==<br />
There are three preferences that can be changed from Wireshark's Preferences - Protocols - LLUDP dialog:<br />
* Message template file: Full path to the message_template.msg file used to decode message name & details from the packets. On Windows use double backslash '\\' instead of single blackslash '\' to separate directories (Example "C:\\Program Files\\FirestormOS-Releasex64\\app_settings\\message_template.msg").<br />
* UDP port range start: First UDP port to mark as LLUDP packets. (default 13000)<br />
* UDP port range end: Last UDP port to mark as LLUDP packets. (default 13050)<br />
<br />
If your OpenSimulator regions are using only ports 9000-9050 then change the UDP port range.<br />
<br />
=== Code license ===<br />
The Wireshark LLUDP Dissector maybe used under the terms of the "Simplified BSD License" or the [https://github.com/Neopallium/lludp_dissector/blob/master/LICENSE_GPL.txt GPL].</div>Aiaustinhttp://opensimulator.org/wiki/LLUDP_DissectorLLUDP Dissector2023-01-14T17:46:34Z<p>Aiaustin: /* LLUDP protocol dissector */ removing code and point just to latest version on GitHub</p>
<hr />
<div>__NOTOC__<br />
{{Quicklinks}}<br />
<br /><br />
<br />
== LLUDP protocol dissector ==<br />
This page describes the Wireshark LLUDP Protocol Dissector that can parse the message_template.msg file and use that information to decode all the message fields from the Linden Lab UDP protocol. Up to date code is available on [https://github.com/Neopallium/lludp_dissector GitHub]<br />
<br />
== Installing ==<br />
* Requires Wireshark with Lua support. This is enabled by default in recent versions of Wireshark. [http://wiki.wireshark.org/Lua See this page for Wireshark support for Lua]<br />
<br />
=== on Linux ===<br />
* Copy all five source files into ~/.wireshark<br />
* '''Check if correct''' If you need to run Wireshark as the root user or using sudo then you will need to edit the scripts into one file by replacing the dofile("script.lua") calls with the contents of file between the quotes.<br />
* '''Check if correct''' The other method is to add your user account to the correct group (on Gentoo it is group "wireshark") that will allow your non-root user to capture packets.<br />
<br />
=== on Windows ===<br />
* Copy the lludp folder containing the five .lua source files into the Wireshark AppData directory <br />
<br />
'''Windows versions up to Windows 11'''<br />
<br />
C:\Users\<username>\AppData\Roaming\Wireshark\Plugins <br />
<br />
'''XP/2000'''<br />
<br />
C:\users\<username>\AppData\Wireshark<br />
<br />
* init.lua is not required as LUA support is enabled by default in recent versions of Wireshark.<br />
<br />
== LLUDP preferences ==<br />
There are three preferences that can be changed from Wireshark's Preferences - Protocols - LLUDP dialog:<br />
* Message template file: Full path to the message_template.msg file used to decode message name & details from the packets. On Windows use double backslash '\\' instead of single blackslash '\' to separate directories (Example "C:\\Program Files\\FirestormOS-Releasex64\\app_settings\\message_template.msg").<br />
* UDP port range start: First UDP port to mark as LLUDP packets. (default 13000)<br />
* UDP port range end: Last UDP port to mark as LLUDP packets. (default 13050)<br />
<br />
If your OpenSimulator regions are using only ports 9000-9050 then change the UDP port range.<br />
<br />
== Description of source files ==<br />
* "init.lua" -- simple script that loads the "lludp.lua" script.<br />
* "lludp.lua" -- contains the code that decodes each packet header and decompresses zero-encoded packets. This file uses wireshark only functions for accessing packet bytes and building a tree of information from each packet.<br />
* "llmessage.lua" -- contains the message_template.msg file parser the decodes the tokens from the lexer into an tree of tables containing all details about each message/block/variable from the template file. This file only has pure lua code.<br />
* "lexer.lua" -- contains the template file lexer. This lexer knows how to tokenize the template file into the follow tokens: IDENTIFIER, NUMBER, COMMENT, EOL. The stream of tokens produced by this lexer is parsed by the "llmessage.lua" file. This file only has pure lua code.<br />
<br />
=== Code license ===<br />
This Wireshark dissector maybe used under the terms of the "Simplified BSD License" or the [http://www.gnu.org/licenses/gpl.txt GPL]. -- Robert G. Jakabosky <bobby@sharedrealm.com><br />
<pre><br />
Simplified BSD License:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.<br />
<br />
</pre><br />
<br />
=== File "init.lua" ===<br />
<source lang="lua"><br />
<br />
-- register http to handle tcp ports 8000-8010 and 9000<br />
do<br />
local tcp_port_table = DissectorTable.get("tcp.port")<br />
local http_dissector = tcp_port_table:get_dissector(80)<br />
for port = 8000,8010 do<br />
tcp_port_table:add(port,http_dissector)<br />
end<br />
tcp_port_table:add(9000,http_dissector)<br />
end<br />
<br />
-- Load lludp protocol dissector.<br />
dofile("lludp.lua")<br />
<br />
</source><br />
<br />
=== File "lludp.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
dofile("llmessage.lua")<br />
<br />
-- cache globals to local for speed.<br />
local str_format=string.format<br />
<br />
-- test if ByteArray only has printable ASCII character and ends with '\0'<br />
local allowed_special = {<br />
[0] = true, -- null<br />
[9] = true, -- tab<br />
[10] = true, -- new line<br />
[13] = true, -- carriage return<br />
}<br />
local function is_string(bytes)<br />
local c<br />
local max = bytes:len() - 1<br />
for i=0,max do<br />
c = bytes:get_index(i)<br />
if c >= 127 then -- not ascii character<br />
return false<br />
elseif c < 32 then -- control character range<br />
if not allowed_special[c] then<br />
-- control characters between NULL and Space<br />
return false<br />
elseif c == 0 and i < max then<br />
-- null byte only allowed at end.<br />
return false<br />
end<br />
end<br />
end<br />
return true<br />
end<br />
<br />
-- lludp protocol example<br />
-- declare our protocol<br />
local lludp_proto = Proto("lludp","LLUDP","LindenLabs UDP Protocol")<br />
<br />
-- setup preferences<br />
lludp_proto.prefs["template_file"] =<br />
Pref.string("Message template file", "message_template.msg", "Message template file")<br />
lludp_proto.prefs["udp_port_start"] =<br />
Pref.string("UDP port range start", "13000", "First UDP port to decode as this protocol")<br />
lludp_proto.prefs["udp_port_end"] =<br />
Pref.string("UDP port range end", "13050", "Last UDP port to decode as this protocol")<br />
-- current preferences settings.<br />
local current_settings = {<br />
template_file = "",<br />
udp_port_start = -1,<br />
udp_port_end = -1,<br />
}<br />
-- current list of parsed messages.<br />
local message_details = nil<br />
<br />
-- setup protocol fields.<br />
lludp_proto.fields = {}<br />
local fds = lludp_proto.fields<br />
fds.flags = ProtoField.uint8("lludp.flags", "Flags", base.HEX, nil, 0xFF)<br />
fds.flags_zero = ProtoField.uint8("lludp.flags.zero", "Zero", base.HEX, nil, 0x80)<br />
fds.flags_reliable = ProtoField.uint8("lludp.flags.rel", "Reliable", base.HEX, nil, 0x40)<br />
fds.flags_resent = ProtoField.uint8("lludp.flags.res", "Resent", base.HEX, nil, 0x20)<br />
fds.flags_ack = ProtoField.uint8("lludp.flags.ack", "Ack", base.HEX, nil, 0x10)<br />
fds.sequence = ProtoField.uint32("lludp.sequence", "Sequence", base.DEC)<br />
fds.extra_len = ProtoField.uint8("lludp.extra_len", "Extra length", base.DEC)<br />
fds.extra_bytes = ProtoField.bytes("lludp.extra_bytes", "Extra header", base.HEX)<br />
fds.msg_id = ProtoField.uint32("lludp.msg.id", "Message ID", base.HEX)<br />
fds.msg_name = ProtoField.bytes("lludp.msg.name", "Message name")<br />
fds.msg = ProtoField.bytes("lludp.msg", "Message body", base.HEX)<br />
fds.acks_count = ProtoField.uint8("lludp.acks_count", "Acks count", base.DEC)<br />
fds.acks = ProtoField.uint32("lludp.acks", "Acks", base.DEC)<br />
fds.block_count = ProtoField.uint8("lludp.block_count", "Block count", base.DEC)<br />
fds.block = ProtoField.bytes("lludp.block", "Block", base.HEX)<br />
fds.var_fixed = ProtoField.bytes("lludp.var.fixed", "Fixed blob", base.HEX)<br />
fds.var_variable = ProtoField.bytes("lludp.var.variable", "Variable blob", base.HEX)<br />
fds.var_string = ProtoField.stringz("lludp.var.string", "String")<br />
fds.var_u8 = ProtoField.uint8("lludp.var.u8", "U8", base.DEC)<br />
fds.var_u16 = ProtoField.uint16("lludp.var.u16", "U16", base.DEC)<br />
fds.var_u32 = ProtoField.uint32("lludp.var.u32", "U32", base.DEC)<br />
fds.var_u64 = ProtoField.uint64("lludp.var.u64", "U64", base.DEC)<br />
fds.var_s8 = ProtoField.int8("lludp.var.s8", "S8", base.DEC)<br />
fds.var_s16 = ProtoField.int16("lludp.var.s16", "S16", base.DEC)<br />
fds.var_s32 = ProtoField.int32("lludp.var.s32", "S32", base.DEC)<br />
fds.var_s64 = ProtoField.int64("lludp.var.s64", "S64", base.DEC)<br />
fds.var_f32 = ProtoField.float("lludp.var.f32", "F32", base.DEC)<br />
fds.var_f64 = ProtoField.double("lludp.var.f64", "F64", base.DEC)<br />
fds.var_llvector3 = ProtoField.bytes("lludp.var.llvector3", "LLVector3", base.HEX)<br />
fds.var_llvector3d = ProtoField.bytes("lludp.var.llvector3d", "LLVector3d", base.HEX)<br />
fds.var_llvector4 = ProtoField.bytes("lludp.var.llvector4", "LLVector4", base.HEX)<br />
fds.var_llquaternion = ProtoField.bytes("lludp.var.llquaternion", "LLQuaternion", base.HEX)<br />
fds.var_lluuid = ProtoField.bytes("lludp.var.lluuid", "LLUUID", base.HEX)<br />
fds.var_bool = ProtoField.uint8("lludp.var.bool", "BOOL", base.DEC)<br />
fds.var_ipaddr = ProtoField.ipv4("lludp.var.ipaddr", "IPADDR", base.DEC)<br />
fds.var_ipport = ProtoField.uint16("lludp.var.ipport", "IPPORT", base.DEC)<br />
-- variable type handlers.<br />
local variable_handlers = {<br />
Fixed = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_fixed, rang)<br />
if len <= 4 then<br />
ti:set_text(str_format("%s: 0x%08x",var.name, rang:uint()))<br />
else<br />
ti:set_text(str_format("%s: length=%d, Blob:%s", var.name, len, tostring(rang)))<br />
end<br />
end,<br />
Variable = function(block_tree, buffer, offset, len, var)<br />
local is_data = false<br />
-- try to guess if this field is text.<br />
if var.name:find("Data") then<br />
-- this is a data find.<br />
is_data = true<br />
end<br />
local str_rang = buffer(offset + var.count_length, len - var.count_length)<br />
local bytes = str_rang:bytes()<br />
if not is_data and is_string(bytes) then<br />
local str = str_rang:string()<br />
local ti = block_tree:add(fds.var_string, buffer(offset, len))<br />
ti:set_text(str_format("%s: %s", var.name, str))<br />
else<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_variable, rang)<br />
if len <= 4 then<br />
ti:set_text(str_format("%s: 0x%08x",var.name, rang:uint()))<br />
else<br />
ti:set_text(str_format("%s: length=%d, Blob:%s", var.name, len, tostring(rang)))<br />
end<br />
end<br />
end,<br />
U8 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u8, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U16 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u16, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u32, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u64, rang)<br />
ti:set_text(str_format("%s: 0x%s",var.name, tostring(rang)))<br />
end,<br />
S8 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s8, rang)<br />
local num = rang:le_uint()<br />
if num > 127 then num = num - 256 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S16 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s16, rang)<br />
local num = rang:le_uint()<br />
if num > 32768 then num = num - 65536 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s32, rang)<br />
local num = rang:le_uint()<br />
if num > 2147483648 then num = num - 4294967296 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s64, rang)<br />
ti:set_text(str_format("%s: 0x%s",var.name, tostring(rang)))<br />
end,<br />
F32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_f32, rang)<br />
ti:set_text(str_format("%s: %f", var.name, rang:le_float()))<br />
end,<br />
F64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_f64, rang)<br />
ti:set_text(str_format("%s: %f", var.name, rang:le_float()))<br />
end,<br />
LLVector3 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector3, rang)<br />
-- parse LLVector3<br />
local x,y,z<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f>", var.name, x, y, z))<br />
end,<br />
LLVector3d = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector3d, rang)<br />
-- parse LLVector3d<br />
local x,y,z<br />
x = buffer(offset + 0,8):le_float()<br />
y = buffer(offset + 8,8):le_float()<br />
z = buffer(offset + 16,8):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f>", var.name, x, y, z))<br />
end,<br />
LLVector4 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector4, rang)<br />
-- parse LLVector4<br />
local x,y,z,s<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
s = buffer(offset + 12,4):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f,%f>", var.name, x, y, z, s))<br />
end,<br />
LLQuaternion = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llquaternion, rang)<br />
-- parse LLQuaternion<br />
local x,y,z,w<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
-- calculate W<br />
w = 1 - (x * x) - (y * y) - (z * z)<br />
if w > 0 then<br />
w = math.sqrt(w)<br />
else<br />
w = 0<br />
end<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f,%f>", var.name, x, y, z, w))<br />
end,<br />
LLUUID = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_lluuid, rang)<br />
local str = tostring(rang)<br />
str = str:sub(1,8) .. '-' ..<br />
str:sub(9,12) .. '-' .. str:sub(13,16) .. '-' ..<br />
str:sub(17,20) .. '-' .. str:sub(21)<br />
ti:set_text(str_format("%s: %s", var.name, str))<br />
end,<br />
BOOL = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_bool, rang)<br />
local val = "false"<br />
if rang:le_uint() > 0 then<br />
val = "true"<br />
end<br />
ti:set_text(str_format("%s: %s", var.name, val))<br />
end,<br />
IPADDR = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add(fds.var_ipaddr, rang)<br />
ti:set_text(str_format("%s: %s", var.name, tostring(rang:ipv4())))<br />
end,<br />
IPPORT = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add(fds.var_ipport, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:uint()))<br />
end,<br />
}<br />
<br />
-- un-register lludp to handle udp port range<br />
local function unregister_udp_port_range(start_port, end_port)<br />
if not start_port or start_port <= 0 or not end_port or end_port <= 0 then<br />
return<br />
end<br />
udp_port_table = DissectorTable.get("udp.port")<br />
for port = start_port,end_port do<br />
udp_port_table:remove(port,lludp_proto)<br />
end<br />
end<br />
<br />
-- register lludp to handle udp port range<br />
local function register_udp_port_range(start_port, end_port)<br />
if not start_port or start_port <= 0 or not end_port or end_port <= 0 then<br />
return<br />
end<br />
udp_port_table = DissectorTable.get("udp.port")<br />
for port = start_port,end_port do<br />
udp_port_table:add(port,lludp_proto)<br />
end<br />
end<br />
<br />
-- handle preferences changes.<br />
function lludp_proto.init(arg1, arg2)<br />
local old_start, old_end<br />
local new_start, new_end<br />
-- check if preferences have changed.<br />
for pref_name,old_v in pairs(current_settings) do<br />
local new_v = lludp_proto.prefs[pref_name]<br />
if new_v ~= old_v then<br />
if pref_name == "template_file" then<br />
-- load & parse message_template.msg file.<br />
local file = new_v<br />
if file and file:len() > 0 then<br />
local new_details = parse_template(file)<br />
if new_details then<br />
message_details = new_details<br />
end<br />
end<br />
elseif pref_name == "udp_port_start" then<br />
old_start = old_v<br />
new_start = new_v<br />
elseif pref_name == "udp_port_end" then<br />
old_end = old_v<br />
new_end = new_v<br />
end<br />
-- save new value.<br />
current_settings[pref_name] = new_v<br />
end<br />
end<br />
-- un-register old port range<br />
if old_start and old_end then<br />
unregister_udp_port_range(tonumber(old_start), tonumber(old_end))<br />
end<br />
-- register new port range.<br />
if new_start and new_end then<br />
register_udp_port_range(tonumber(new_start), tonumber(new_end))<br />
end<br />
end<br />
<br />
-- parse flag bits.<br />
local FLAG_ZER = 4<br />
local FLAG_REL = 3<br />
local FLAG_RES = 2<br />
local FLAG_ACK = 1<br />
local flag_names = {"ACK", "RES", "REL", "ZER"}<br />
local bits_lookup = {<br />
{},<br />
{1},<br />
{2},<br />
{2,1},<br />
{3},<br />
{3,1},<br />
{3,2},<br />
{3,2,1},<br />
{4},<br />
{4,1},<br />
{4,2},<br />
{4,2,1},<br />
{4,3},<br />
{4,3,1},<br />
{4,3,2},<br />
{4,3,2,1},<br />
}<br />
local function parse_flags(flags)<br />
flags = (flags / 16) + 1<br />
local bit_list = bits_lookup[flags]<br />
local bits = {}<br />
local names = ""<br />
for _, bit in ipairs(bit_list) do<br />
bits[bit] = true<br />
if names:len() > 0 then<br />
names = names .. ", "<br />
end<br />
names = names .. flag_names[bit]<br />
end<br />
return bits, names<br />
end<br />
<br />
local function grow_buff(buff, size)<br />
local old_size = buff:len()<br />
if old_size > size then return end<br />
-- buffer needs to grow<br />
buff:set_size(size)<br />
-- fill new space with zeros<br />
for i = old_size,size-1 do<br />
buff:set_index(i, 0)<br />
end<br />
end<br />
<br />
local function zero_decode(zero_buf)<br />
local out_buf = ByteArray.new()<br />
local zero_off = 0<br />
local zero_len = zero_buf:len()<br />
local out_size = 0<br />
local out_off = 0<br />
local b<br />
-- pre-allocate<br />
grow_buff(out_buf, zero_len)<br />
out_size = zero_len<br />
-- zero expand<br />
repeat<br />
b = zero_buf:get_index(zero_off)<br />
if b == 0 then<br />
-- get zero count<br />
local count = zero_buf:get_index(zero_off + 1)<br />
if count == 0 then count = 255 end<br />
out_off = out_off + count<br />
-- fill zeros<br />
if out_off > out_size then<br />
out_size = out_off + 128<br />
grow_buff(out_buf, out_size)<br />
end<br />
zero_off = zero_off + 2<br />
else<br />
if out_off >= out_size then<br />
out_size = out_off + (zero_len - zero_off) + 4<br />
grow_buff(out_buf, out_size)<br />
end<br />
-- copy non-zero bytes.<br />
out_buf:set_index(out_off,b)<br />
zero_off = zero_off + 1<br />
out_off = out_off + 1<br />
end<br />
until zero_off == zero_len<br />
-- truncate to real size.<br />
out_buf:set_size(out_off)<br />
<br />
return out_buf:tvb("Decompressed Data")<br />
end<br />
<br />
local function parse_msg_id(buff)<br />
local b = buff:get_index(0)<br />
local msg_id = b<br />
local msg_id_len = 1<br />
if b == 255 then<br />
b = buff:get_index(1)<br />
msg_id = msg_id * 256 + b<br />
msg_id_len = 2<br />
if b == 255 then<br />
b = buff:get_index(2)<br />
msg_id = msg_id * 256 + b<br />
b = buff:get_index(3)<br />
msg_id = msg_id * 256 + b<br />
msg_id_len = 4<br />
end<br />
end<br />
return msg_id, msg_id_len<br />
end<br />
<br />
-- get message name.<br />
local function get_msg_name(msg_id)<br />
-- check that we have message details<br />
if message_details == nil then<br />
return str_format("0x%08x", msg_id)<br />
end<br />
-- find message name from id.<br />
local msg = message_details.msgs[msg_id]<br />
-- Invalid message id<br />
if msg == nil then<br />
return str_format("0x%08x", msg_id)<br />
end<br />
return msg.name<br />
end<br />
<br />
-- calculate length a block.<br />
local function get_block_length(msg_buffer, start_offset, block)<br />
-- check if bock is fixed length.<br />
if block.fixed_length then<br />
return block.min_length<br />
end<br />
-- parse block's variables to calculate total block length.<br />
local offset = start_offset<br />
local rang<br />
for _,var in ipairs(block) do<br />
local len = 0<br />
if var.has_count then<br />
-- variable with length bytes.<br />
len = var.count_length<br />
--print(var.name, offset, ", len:", len)<br />
rang = msg_buffer(offset, len)<br />
len = len + rang:le_uint()<br />
--print(var.name, var.count_length, ", total:", len)<br />
else<br />
-- fixed length variable<br />
len = var.length<br />
--print(var.name, ", total:", len)<br />
end<br />
offset = offset + len<br />
end<br />
return (offset - start_offset)<br />
end<br />
<br />
-- build block tree<br />
local function build_block_tree(msg_buffer, block_tree, start_offset, block)<br />
local offset = start_offset<br />
local rang<br />
-- parse block's variables<br />
for _,var in ipairs(block) do<br />
local len = 0<br />
if var.has_count then<br />
-- variable with length bytes.<br />
len = var.count_length<br />
rang = msg_buffer(offset, len)<br />
len = len + rang:le_uint()<br />
else<br />
-- fixed length variable<br />
len = var.length<br />
end<br />
-- get variable's type field.<br />
local handler = variable_handlers[var.type]<br />
-- parse variable.<br />
if handler then<br />
handler(block_tree, msg_buffer, offset, len, var)<br />
end<br />
offset = offset + len<br />
end<br />
return (offset - start_offset)<br />
end<br />
<br />
-- buid message tree<br />
local function build_msg_tree(msg_buffer, msg_tree, msg_id)<br />
local offset = 0<br />
local rang<br />
-- check that we have message details<br />
if message_details == nil then<br />
msg_tree:set_text(str_format("Message Id: 0x%08x", msg_id))<br />
return nil<br />
end<br />
-- find message name from id.<br />
local msg = message_details.msgs[msg_id]<br />
-- Invalid message id<br />
if msg == nil then<br />
msg = str_format("Invalid message id: 0x%08x", msg_id)<br />
msg_tree:add_expert_info(PI_MALFORMED, PI_ERROR, msg)<br />
msg_tree:set_text(msg)<br />
return nil<br />
end<br />
-- skip message id bytes.<br />
offset = msg.id_length<br />
-- set message name.<br />
msg_tree:set_text(msg.name .. ":")<br />
-- proccess message blocks<br />
for _,block in ipairs(msg) do<br />
local count = block.count<br />
if count == nil then<br />
-- parse count byte.<br />
rang = msg_buffer(offset,1)<br />
count = rang:uint()<br />
msg_tree:add(fds.block_count,rang)<br />
offset = offset + 1<br />
end<br />
-- print("block name: ", block.name, count)<br />
for n=1,count do<br />
local block_len = get_block_length(msg_buffer, offset, block)<br />
-- parse block<br />
rang = msg_buffer(offset, block_len)<br />
local block_tree = msg_tree:add(fds.block,rang)<br />
if count > 1 then<br />
block_tree:set_text(str_format("%s: %d of %d",block.name,n,count))<br />
else<br />
block_tree:set_text(block.name)<br />
end<br />
-- parse block variables.<br />
build_block_tree(msg_buffer, block_tree, offset, block)<br />
offset = offset + block_len<br />
end<br />
end<br />
return msg.name<br />
end<br />
<br />
-- packet dissector<br />
function lludp_proto.dissector(buffer,pinfo,tree)<br />
local rang,offset<br />
pinfo.cols.protocol = "LLUDP"<br />
local lludp_tree = tree:add(lludp_proto,buffer(),"Linden UDP Protocol")<br />
-- Flags byte.<br />
offset = 0<br />
rang = buffer(offset,1)<br />
local flags = rang:uint()<br />
local flags_bits, flags_list = parse_flags(flags)<br />
flags_tree = lludp_tree:add(fds.flags, rang)<br />
flags_tree:set_text("Flags: " .. str_format('0x%02X (%s)', flags, flags_list))<br />
flags_tree:add(fds.flags_zero, rang)<br />
flags_tree:add(fds.flags_reliable, rang)<br />
flags_tree:add(fds.flags_resent, rang)<br />
flags_tree:add(fds.flags_ack, rang)<br />
offset = offset + 1<br />
-- Sequence number 4 bytes.<br />
rang = buffer(offset,4)<br />
local sequence = rang:uint()<br />
lludp_tree:add(fds.sequence, rang)<br />
offset = offset + 4<br />
-- Extra header length.<br />
rang = buffer(offset,1)<br />
local extra_length = rang:uint()<br />
lludp_tree:add(fds.extra_len,rang)<br />
offset = offset + 1<br />
-- Extra header data.<br />
if extra_length > 0 then<br />
rang = buffer(offset, extra_length)<br />
lludp_tree:add(fds.extra_bytes, rang)<br />
offset = offset + extra_length<br />
end<br />
-- Appended Acks. count<br />
local acks_bytes = 0<br />
local acks_count = 0<br />
if flags_bits[FLAG_ACK] then<br />
rang = buffer(buffer:len() - 1, 1)<br />
acks_count = rang:uint()<br />
acks_bytes = (acks_count * 4) + 1<br />
end<br />
-- Zero Decode<br />
local msg_len = (buffer:len() - acks_bytes) - offset<br />
if flags_bits[FLAG_ZER] then<br />
msg_buffer=zero_decode(buffer(offset,msg_len):bytes())<br />
msg_len = msg_buffer:len()<br />
offset = 0<br />
else<br />
msg_buffer = buffer(offset, msg_len):tvb()<br />
offset = 0<br />
end<br />
-- Message ID<br />
local msg_id, msg_id_len = -1, 4<br />
if msg_id_len > msg_len then<br />
msg_id_len = msg_len<br />
end<br />
msg_id, msg_id_len = parse_msg_id(msg_buffer(offset, msg_id_len):bytes())<br />
rang = msg_buffer(offset, msg_id_len)<br />
local msg_id_tree = lludp_tree:add(fds.msg_id, rang)<br />
local msg_name = get_msg_name(msg_id)<br />
if msg_name == nil then<br />
msg_id_tree:set_text(str_format("Message name: 0x%08x", msg_id))<br />
else<br />
msg_id_tree:set_text(str_format("Message name: %s",msg_name))<br />
end<br />
-- Message body.<br />
rang = msg_buffer(offset, msg_len)<br />
local msg_tree = lludp_tree:add(fds.msg, rang)<br />
build_msg_tree(msg_buffer, msg_tree, msg_id)<br />
-- Appended Acks. list.<br />
if flags_bits[FLAG_ACK] then<br />
local acks_off = buffer:len()<br />
rang = buffer(acks_off - 1, 1)<br />
acks_off = acks_off - acks_bytes<br />
local acks_tree = lludp_tree:add(fds.acks_count, rang)<br />
for i = 1,acks_count do<br />
rang = buffer(acks_off,4)<br />
acks_tree:add(fds.acks, rang)<br />
acks_off = acks_off + 4<br />
end<br />
end<br />
-- Info column<br />
pinfo.cols.info = str_format('[%s] Seq=%u Type=%s', flags_list, sequence, msg_name)<br />
end<br />
<br />
-- register lludp to handle udp ports 9000-9003<br />
register_udp_port_range(9000,9003)<br />
</source><br />
<br />
=== File "llmessage.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
dofile("lexer.lua")<br />
<br />
local lexer<br />
local cur_token = nil<br />
local cur_token_str = nil<br />
<br />
local function get_token(skip_tokens)<br />
repeat<br />
token = lexer.get_token()<br />
if token ~= nil then<br />
cur_token = token[1]<br />
cur_token_str = token[2]<br />
end<br />
until token == nil or not skip_tokens[cur_token]<br />
return token<br />
end<br />
<br />
local function run_parser(parser)<br />
local state = parser.init()<br />
if parser.skip_tokens == nil then parser.skip_tokens = {} end<br />
while get_token(parser.skip_tokens) do<br />
-- check what the parser is expecting next.<br />
if state.expect then<br />
-- check expected type<br />
if state.expect ~= cur_token then<br />
error(string.format("state.expected token '%s' instead of '%s'",<br />
TokenNames[state.expect], TokenNames[cur_token]))<br />
end<br />
-- reset expect field<br />
state.expect = nil<br />
end<br />
if state.expect_str then<br />
-- check expected string<br />
if state.expect_str ~= cur_token_str then<br />
error(string.format("state.expected token '%s' instead of '%s'",<br />
state.expect_str, cur_token_str))<br />
end<br />
-- reset expect_str field<br />
state.expect_str = nil<br />
end<br />
-- get handler function for current token.<br />
local f = parser[cur_token]<br />
if f ~= nil then<br />
local ret = f(state)<br />
if ret then<br />
-- praser finished.<br />
return ret<br />
end<br />
elseif parser.unhandled_error then<br />
error(string.format("unhandled token '%s' when paring '%s'\n", cur_token_str, parser.name))<br />
end<br />
end<br />
return parser.eof(state)<br />
end<br />
<br />
-- Known variable types and there fixed length.<br />
-- length == -1, requires a number after the type that is the length of count field<br />
-- length == -2, requires a number after the type that is the fixed variable length.<br />
VariableTypes = {<br />
Null = 0,<br />
Fixed = -2,<br />
Variable = -1,<br />
U8 = 1,<br />
U16 = 2,<br />
U32 = 4,<br />
U64 = 8,<br />
S8 = 1,<br />
S16 = 2,<br />
S32 = 4,<br />
S64 = 8,<br />
F32 = 4,<br />
F64 = 8,<br />
LLVector3 = 12,<br />
LLVector3d = 24,<br />
LLVector4 = 16,<br />
LLQuaternion = 12,<br />
LLUUID = 16,<br />
BOOL = 1,<br />
IPADDR = 4,<br />
IPPORT = 2,<br />
}<br />
<br />
--<br />
-- Variable parser<br />
--<br />
local variable_parser = {<br />
name = "variable",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
return {<br />
name = "<MISSING VARIABLE NAME>",<br />
type = "Null",<br />
has_count = false,<br />
length = 0,<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
required = 2<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "type"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "type" then<br />
state.type = cur_token_str<br />
state.length = VariableTypes[state.type]<br />
if state.length == nil then<br />
error("Unknown variable type: " .. cur_token_str)<br />
elseif state.length == -1 or state.length == -2 then<br />
state.expect = Token.NUMBER<br />
else<br />
state.required = state.required - 1<br />
end<br />
else<br />
error(string.format("unhandled variable identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "type" then<br />
if state.length == -1 then<br />
-- variable field length uses embedded count field<br />
state.has_count = true<br />
state.count_length = tonumber(cur_token_str)<br />
state.length = nil<br />
elseif state.length == -2 then<br />
-- fixed field length<br />
state.length = tonumber(cur_token_str)<br />
end<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled variable number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
error("sub block not allowed in variable block")<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of variable: " .. state.name)<br />
end,<br />
}<br />
<br />
-- Block Quantities<br />
local BlockQuantity = {<br />
Single = 1,<br />
Variable = -1,<br />
Multiple = -2,<br />
}<br />
<br />
--<br />
-- Block parser<br />
--<br />
local block_parser = {<br />
name = "block",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
return {<br />
name = "<MISSING BLOCK NAME>",<br />
quantity = "Single",<br />
count = 0,<br />
min_length = 0,<br />
fixed_length = true,<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
required = 2<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "quantity"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "quantity" then<br />
state.quantity = cur_token_str<br />
state.count = BlockQuantity[cur_token_str]<br />
if state.count == nil then<br />
error("Unknown block quantity: " .. cur_token_str)<br />
elseif state.count == -2 then<br />
state.expect_field = "count"<br />
state.expect = Token.NUMBER<br />
else<br />
if state.count == -1 then<br />
state.has_count = true<br />
state.count_length = 1<br />
state.count = nil<br />
end<br />
state.required = state.required - 1<br />
end<br />
else<br />
error(string.format("unhandled block identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "count" then<br />
state.count = tonumber(cur_token_str)<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled block number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local variable = run_parser(variable_parser)<br />
table.insert(state,variable)<br />
-- add length of fixed length variables to minimal length of block.<br />
if variable.has_count then<br />
state.min_length = state.min_length + variable.count_length<br />
state.fixed_length = false<br />
else<br />
state.min_length = state.min_length + variable.length<br />
end<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of block: " .. state.name)<br />
end,<br />
}<br />
<br />
--<br />
-- Message parser<br />
--<br />
local message_parser = {<br />
name = "message",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
-- create state<br />
return {<br />
name = "<MISSING MESSAGE NAME>",<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
fixed_length = true,<br />
min_length = 0,<br />
required = 5<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "frequency"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "frequency" then<br />
state.frequency = cur_token_str<br />
state.expect = Token.NUMBER<br />
state.required = state.required - 1<br />
elseif state.expect_field == "trust" then<br />
state.trust = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "compression"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "compression" then<br />
state.compression = cur_token_str<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled message identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "frequency" then<br />
state.number = tonumber(cur_token_str)<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "trust"<br />
state.required = state.required - 1<br />
-- create true message id from frequency and message number<br />
local freq = state.frequency<br />
if freq == "High" then<br />
-- High is already correct.<br />
state.id = state.number<br />
state.id_length = 1<br />
elseif freq == "Medium" then<br />
state.id = tonumber("0xFF" .. string.format("%02X", state.number))<br />
state.id_length = 2<br />
elseif freq == "Low" then<br />
state.id = tonumber("0xFFFF" .. string.format("%04X", state.number))<br />
state.id_length = 4<br />
else<br />
-- Fixed is already correct.<br />
state.id = state.number<br />
state.id_length = 4<br />
end<br />
else<br />
error(string.format("unhandled message number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local block = run_parser(block_parser)<br />
table.insert(state,block)<br />
-- add min length of block to minimal length of message<br />
local min_length = block.min_length<br />
if block.has_count then<br />
-- add one byte for the block count<br />
min_length = min_length + 1<br />
state.fixed_length = false<br />
else<br />
-- if block is not fixed length then message can't be fixed length.<br />
if not block.fixed_length then<br />
state.fixed_length = false<br />
end<br />
min_length = min_length * block.count<br />
end<br />
state.min_length = state.min_length + min_length<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of message: " .. state.name)<br />
end,<br />
}<br />
<br />
--<br />
-- Template file parser<br />
--<br />
local template_parser = {<br />
name = "message_template",<br />
unhandled_error = false,<br />
init = function()<br />
return {<br />
version = 0,<br />
msg_count = 0,<br />
msgs = {}<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
-- handle version<br />
if cur_token_str == "version" then<br />
state.expect = Token.NUMBER<br />
state.last_ident = cur_token_str<br />
else<br />
error(string.format("unknown template identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
-- handle version number<br />
if state.last_ident == "version" then<br />
state.last_ident = nil<br />
state.version = tonumber(cur_token_str)<br />
-- check version number<br />
if state.version ~= 2 then<br />
error("invalid verion: " .. state.version)<br />
end<br />
else<br />
error(string.format("unhandled template number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local message = run_parser(message_parser)<br />
state.msg_count = state.msg_count + 1<br />
state.msgs[message.id] = message<br />
end,<br />
["}"] = function(state)<br />
error(string.format("unhandled '%s' token",cur_token_str))<br />
end,<br />
eof = function(state)<br />
return state<br />
end,<br />
}<br />
<br />
function parse_template(file)<br />
-- create lexer<br />
local status, ret = pcall(get_lexer,file)<br />
if not status then<br />
ret = string.format("LLUDP: Failed parse file into tokens: %s\n%s\n", file, ret)<br />
error(ret, 0)<br />
return nil<br />
end<br />
lexer = ret<br />
-- parse template file<br />
local status, ret = pcall(run_parser,template_parser)<br />
if not status then<br />
ret = string.format("LLUDP: Failed parsing on line %s:%d: '%s'\n%s\n",<br />
file, lexer.get_line_number(), lexer.get_line(), ret)<br />
error(ret, 0)<br />
return nil<br />
end<br />
io.write("finished parsing: " .. file .. "\n")<br />
-- return list of messages parsed from file.<br />
return ret<br />
end<br />
<br />
--parse_template("message_template.msg")<br />
--print_tokens("message_template.msg")<br />
<br />
</source><br />
<br />
=== File "lexer.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
-- Token types.<br />
Token = {<br />
NONE = -1,<br />
IDENTIFIER = -2,<br />
NUMBER = -3,<br />
COMMENT = -6,<br />
EOL = -7,<br />
["{"] = "{",<br />
["}"] = "}",<br />
}<br />
TokenNames = {<br />
[-1] = "NONE",<br />
[-2] = "IDENTIFIER",<br />
[-3] = "NUMBER",<br />
[-6] = "COMMENT",<br />
[-7] = "EOL",<br />
["{"] = "{",<br />
["}"] = "}",<br />
}<br />
<br />
-- parse line into array of tokens.<br />
local function default_parse_tokens(line)<br />
local tokens = {}<br />
local comment = nil<br />
-- check for a comment on this line.<br />
local idx = line:find("//")<br />
if idx ~= nil then<br />
comment = {Token.COMMENT, line:sub(idx)}<br />
-- remove comment from line<br />
line = line:sub(1,idx - 1)<br />
end<br />
<br />
-- split line into tokens using white-space as token delimitator<br />
for tok in line:gmatch("%s?([^%s]+)") do<br />
local tok_type = Token.NONE<br />
-- check for number<br />
if tonumber(tok) ~= nil then<br />
tok_type = Token.NUMBER<br />
elseif Token[tok] then<br />
-- token is same as type<br />
tok_type = Token[tok]<br />
else<br />
-- token is an identifier<br />
tok_type = Token.IDENTIFIER<br />
end<br />
table.insert(tokens,{tok_type,tok})<br />
end<br />
-- insert comment token.<br />
if comment ~= nil then<br />
table.insert(tokens,comment)<br />
end<br />
-- add token to mark the end of this line<br />
table.insert(tokens,{Token.EOL, ""})<br />
return tokens<br />
end<br />
<br />
function get_lexer(file, parse_tokens)<br />
-- use the default line tokenizer if one is not provided<br />
if parse_tokens == nil then parse_tokens = default_parse_tokens end<br />
-- next/current line code<br />
local line_num = 0<br />
local line = nil<br />
local next_line = io.lines(file)<br />
-- parse line tokens code<br />
local get_next_token = nil<br />
local next_tokens = function ()<br />
local f, tokens, idx<br />
repeat<br />
line_num = line_num + 1<br />
line = next_line()<br />
if line == nil then return nil end<br />
tokens = parse_tokens(line)<br />
until tokens ~= nil<br />
-- create get_next_toekn function from table iterator<br />
f, tokens, idx = ipairs(tokens)<br />
get_next_token = function()<br />
idx, token = f(tokens, idx)<br />
return token<br />
end<br />
return tokens<br />
end<br />
<br />
-- get first group of tokens<br />
if next_tokens() == nil then<br />
-- error reading file or empty file<br />
return nil<br />
end<br />
-- build lexer table.<br />
local lexer = {<br />
get_token = function ()<br />
local token<br />
repeat<br />
token = get_next_token()<br />
if token == nil then<br />
-- get next group of tokens<br />
if next_tokens() == nil then<br />
-- end of file.<br />
return nil<br />
end<br />
end<br />
until token ~= nil<br />
return token<br />
end,<br />
get_line_number = function() return line_num end,<br />
get_line = function() return line end<br />
}<br />
return lexer<br />
end<br />
<br />
function print_tokens(file)<br />
local lexer = get_lexer(file)<br />
local num = -1<br />
while true do<br />
local tok = lexer.get_token()<br />
if tok == nil then<br />
break<br />
end<br />
if num ~= lexer.get_line_number() then<br />
num = lexer.get_line_number()<br />
io.write("\n")<br />
io.write(string.format("%d: ",num))<br />
end<br />
io.write(string.format("%s ",tok[2]))<br />
end<br />
io.write("\n")<br />
end<br />
<br />
</source></div>Aiaustinhttp://opensimulator.org/wiki/LLUDP_DissectorLLUDP Dissector2023-01-14T17:44:24Z<p>Aiaustin: /* LLUDP protocol dissector */</p>
<hr />
<div>__NOTOC__<br />
{{Quicklinks}}<br />
<br /><br />
<br />
== LLUDP protocol dissector ==<br />
On this page you will find the Lua code for a wireshark protocol dissector that can parse the message_template.msg file and use that information to decode all the message fields from the Linden UDP protocol. Up to date code is available on [https://github.com/Neopallium/lludp_dissector GitHub]<br />
<br />
== Installing ==<br />
* Requires Wireshark with Lua support. This is enabled by default in recent versions of Wireshark. [http://wiki.wireshark.org/Lua See this page for Wireshark support for Lua]<br />
<br />
=== on Linux ===<br />
* Copy all five source files into ~/.wireshark<br />
* '''Check if correct''' If you need to run Wireshark as the root user or using sudo then you will need to edit the scripts into one file by replacing the dofile("script.lua") calls with the contents of file between the quotes.<br />
* '''Check if correct''' The other method is to add your user account to the correct group (on Gentoo it is group "wireshark") that will allow your non-root user to capture packets.<br />
<br />
=== on Windows ===<br />
* Copy the lludp folder containing the five .lua source files into the Wireshark AppData directory <br />
<br />
'''Windows versions up to Windows 11'''<br />
<br />
C:\Users\<username>\AppData\Roaming\Wireshark\Plugins <br />
<br />
'''XP/2000'''<br />
<br />
C:\users\<username>\AppData\Wireshark<br />
<br />
* init.lua is not required as LUA support is enabled by default in recent versions of Wireshark.<br />
<br />
== LLUDP preferences ==<br />
There are three preferences that can be changed from Wireshark's Preferences - Protocols - LLUDP dialog:<br />
* Message template file: Full path to the message_template.msg file used to decode message name & details from the packets. On Windows use double backslash '\\' instead of single blackslash '\' to separate directories (Example "C:\\Program Files\\FirestormOS-Releasex64\\app_settings\\message_template.msg").<br />
* UDP port range start: First UDP port to mark as LLUDP packets. (default 13000)<br />
* UDP port range end: Last UDP port to mark as LLUDP packets. (default 13050)<br />
<br />
If your OpenSimulator regions are using only ports 9000-9050 then change the UDP port range.<br />
<br />
== Description of source files ==<br />
* "init.lua" -- simple script that loads the "lludp.lua" script.<br />
* "lludp.lua" -- contains the code that decodes each packet header and decompresses zero-encoded packets. This file uses wireshark only functions for accessing packet bytes and building a tree of information from each packet.<br />
* "llmessage.lua" -- contains the message_template.msg file parser the decodes the tokens from the lexer into an tree of tables containing all details about each message/block/variable from the template file. This file only has pure lua code.<br />
* "lexer.lua" -- contains the template file lexer. This lexer knows how to tokenize the template file into the follow tokens: IDENTIFIER, NUMBER, COMMENT, EOL. The stream of tokens produced by this lexer is parsed by the "llmessage.lua" file. This file only has pure lua code.<br />
<br />
=== Code license ===<br />
This Wireshark dissector maybe used under the terms of the "Simplified BSD License" or the [http://www.gnu.org/licenses/gpl.txt GPL]. -- Robert G. Jakabosky <bobby@sharedrealm.com><br />
<pre><br />
Simplified BSD License:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.<br />
<br />
</pre><br />
<br />
=== File "init.lua" ===<br />
<source lang="lua"><br />
<br />
-- register http to handle tcp ports 8000-8010 and 9000<br />
do<br />
local tcp_port_table = DissectorTable.get("tcp.port")<br />
local http_dissector = tcp_port_table:get_dissector(80)<br />
for port = 8000,8010 do<br />
tcp_port_table:add(port,http_dissector)<br />
end<br />
tcp_port_table:add(9000,http_dissector)<br />
end<br />
<br />
-- Load lludp protocol dissector.<br />
dofile("lludp.lua")<br />
<br />
</source><br />
<br />
=== File "lludp.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
dofile("llmessage.lua")<br />
<br />
-- cache globals to local for speed.<br />
local str_format=string.format<br />
<br />
-- test if ByteArray only has printable ASCII character and ends with '\0'<br />
local allowed_special = {<br />
[0] = true, -- null<br />
[9] = true, -- tab<br />
[10] = true, -- new line<br />
[13] = true, -- carriage return<br />
}<br />
local function is_string(bytes)<br />
local c<br />
local max = bytes:len() - 1<br />
for i=0,max do<br />
c = bytes:get_index(i)<br />
if c >= 127 then -- not ascii character<br />
return false<br />
elseif c < 32 then -- control character range<br />
if not allowed_special[c] then<br />
-- control characters between NULL and Space<br />
return false<br />
elseif c == 0 and i < max then<br />
-- null byte only allowed at end.<br />
return false<br />
end<br />
end<br />
end<br />
return true<br />
end<br />
<br />
-- lludp protocol example<br />
-- declare our protocol<br />
local lludp_proto = Proto("lludp","LLUDP","LindenLabs UDP Protocol")<br />
<br />
-- setup preferences<br />
lludp_proto.prefs["template_file"] =<br />
Pref.string("Message template file", "message_template.msg", "Message template file")<br />
lludp_proto.prefs["udp_port_start"] =<br />
Pref.string("UDP port range start", "13000", "First UDP port to decode as this protocol")<br />
lludp_proto.prefs["udp_port_end"] =<br />
Pref.string("UDP port range end", "13050", "Last UDP port to decode as this protocol")<br />
-- current preferences settings.<br />
local current_settings = {<br />
template_file = "",<br />
udp_port_start = -1,<br />
udp_port_end = -1,<br />
}<br />
-- current list of parsed messages.<br />
local message_details = nil<br />
<br />
-- setup protocol fields.<br />
lludp_proto.fields = {}<br />
local fds = lludp_proto.fields<br />
fds.flags = ProtoField.uint8("lludp.flags", "Flags", base.HEX, nil, 0xFF)<br />
fds.flags_zero = ProtoField.uint8("lludp.flags.zero", "Zero", base.HEX, nil, 0x80)<br />
fds.flags_reliable = ProtoField.uint8("lludp.flags.rel", "Reliable", base.HEX, nil, 0x40)<br />
fds.flags_resent = ProtoField.uint8("lludp.flags.res", "Resent", base.HEX, nil, 0x20)<br />
fds.flags_ack = ProtoField.uint8("lludp.flags.ack", "Ack", base.HEX, nil, 0x10)<br />
fds.sequence = ProtoField.uint32("lludp.sequence", "Sequence", base.DEC)<br />
fds.extra_len = ProtoField.uint8("lludp.extra_len", "Extra length", base.DEC)<br />
fds.extra_bytes = ProtoField.bytes("lludp.extra_bytes", "Extra header", base.HEX)<br />
fds.msg_id = ProtoField.uint32("lludp.msg.id", "Message ID", base.HEX)<br />
fds.msg_name = ProtoField.bytes("lludp.msg.name", "Message name")<br />
fds.msg = ProtoField.bytes("lludp.msg", "Message body", base.HEX)<br />
fds.acks_count = ProtoField.uint8("lludp.acks_count", "Acks count", base.DEC)<br />
fds.acks = ProtoField.uint32("lludp.acks", "Acks", base.DEC)<br />
fds.block_count = ProtoField.uint8("lludp.block_count", "Block count", base.DEC)<br />
fds.block = ProtoField.bytes("lludp.block", "Block", base.HEX)<br />
fds.var_fixed = ProtoField.bytes("lludp.var.fixed", "Fixed blob", base.HEX)<br />
fds.var_variable = ProtoField.bytes("lludp.var.variable", "Variable blob", base.HEX)<br />
fds.var_string = ProtoField.stringz("lludp.var.string", "String")<br />
fds.var_u8 = ProtoField.uint8("lludp.var.u8", "U8", base.DEC)<br />
fds.var_u16 = ProtoField.uint16("lludp.var.u16", "U16", base.DEC)<br />
fds.var_u32 = ProtoField.uint32("lludp.var.u32", "U32", base.DEC)<br />
fds.var_u64 = ProtoField.uint64("lludp.var.u64", "U64", base.DEC)<br />
fds.var_s8 = ProtoField.int8("lludp.var.s8", "S8", base.DEC)<br />
fds.var_s16 = ProtoField.int16("lludp.var.s16", "S16", base.DEC)<br />
fds.var_s32 = ProtoField.int32("lludp.var.s32", "S32", base.DEC)<br />
fds.var_s64 = ProtoField.int64("lludp.var.s64", "S64", base.DEC)<br />
fds.var_f32 = ProtoField.float("lludp.var.f32", "F32", base.DEC)<br />
fds.var_f64 = ProtoField.double("lludp.var.f64", "F64", base.DEC)<br />
fds.var_llvector3 = ProtoField.bytes("lludp.var.llvector3", "LLVector3", base.HEX)<br />
fds.var_llvector3d = ProtoField.bytes("lludp.var.llvector3d", "LLVector3d", base.HEX)<br />
fds.var_llvector4 = ProtoField.bytes("lludp.var.llvector4", "LLVector4", base.HEX)<br />
fds.var_llquaternion = ProtoField.bytes("lludp.var.llquaternion", "LLQuaternion", base.HEX)<br />
fds.var_lluuid = ProtoField.bytes("lludp.var.lluuid", "LLUUID", base.HEX)<br />
fds.var_bool = ProtoField.uint8("lludp.var.bool", "BOOL", base.DEC)<br />
fds.var_ipaddr = ProtoField.ipv4("lludp.var.ipaddr", "IPADDR", base.DEC)<br />
fds.var_ipport = ProtoField.uint16("lludp.var.ipport", "IPPORT", base.DEC)<br />
-- variable type handlers.<br />
local variable_handlers = {<br />
Fixed = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_fixed, rang)<br />
if len <= 4 then<br />
ti:set_text(str_format("%s: 0x%08x",var.name, rang:uint()))<br />
else<br />
ti:set_text(str_format("%s: length=%d, Blob:%s", var.name, len, tostring(rang)))<br />
end<br />
end,<br />
Variable = function(block_tree, buffer, offset, len, var)<br />
local is_data = false<br />
-- try to guess if this field is text.<br />
if var.name:find("Data") then<br />
-- this is a data find.<br />
is_data = true<br />
end<br />
local str_rang = buffer(offset + var.count_length, len - var.count_length)<br />
local bytes = str_rang:bytes()<br />
if not is_data and is_string(bytes) then<br />
local str = str_rang:string()<br />
local ti = block_tree:add(fds.var_string, buffer(offset, len))<br />
ti:set_text(str_format("%s: %s", var.name, str))<br />
else<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_variable, rang)<br />
if len <= 4 then<br />
ti:set_text(str_format("%s: 0x%08x",var.name, rang:uint()))<br />
else<br />
ti:set_text(str_format("%s: length=%d, Blob:%s", var.name, len, tostring(rang)))<br />
end<br />
end<br />
end,<br />
U8 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u8, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U16 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u16, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u32, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u64, rang)<br />
ti:set_text(str_format("%s: 0x%s",var.name, tostring(rang)))<br />
end,<br />
S8 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s8, rang)<br />
local num = rang:le_uint()<br />
if num > 127 then num = num - 256 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S16 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s16, rang)<br />
local num = rang:le_uint()<br />
if num > 32768 then num = num - 65536 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s32, rang)<br />
local num = rang:le_uint()<br />
if num > 2147483648 then num = num - 4294967296 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s64, rang)<br />
ti:set_text(str_format("%s: 0x%s",var.name, tostring(rang)))<br />
end,<br />
F32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_f32, rang)<br />
ti:set_text(str_format("%s: %f", var.name, rang:le_float()))<br />
end,<br />
F64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_f64, rang)<br />
ti:set_text(str_format("%s: %f", var.name, rang:le_float()))<br />
end,<br />
LLVector3 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector3, rang)<br />
-- parse LLVector3<br />
local x,y,z<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f>", var.name, x, y, z))<br />
end,<br />
LLVector3d = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector3d, rang)<br />
-- parse LLVector3d<br />
local x,y,z<br />
x = buffer(offset + 0,8):le_float()<br />
y = buffer(offset + 8,8):le_float()<br />
z = buffer(offset + 16,8):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f>", var.name, x, y, z))<br />
end,<br />
LLVector4 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector4, rang)<br />
-- parse LLVector4<br />
local x,y,z,s<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
s = buffer(offset + 12,4):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f,%f>", var.name, x, y, z, s))<br />
end,<br />
LLQuaternion = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llquaternion, rang)<br />
-- parse LLQuaternion<br />
local x,y,z,w<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
-- calculate W<br />
w = 1 - (x * x) - (y * y) - (z * z)<br />
if w > 0 then<br />
w = math.sqrt(w)<br />
else<br />
w = 0<br />
end<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f,%f>", var.name, x, y, z, w))<br />
end,<br />
LLUUID = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_lluuid, rang)<br />
local str = tostring(rang)<br />
str = str:sub(1,8) .. '-' ..<br />
str:sub(9,12) .. '-' .. str:sub(13,16) .. '-' ..<br />
str:sub(17,20) .. '-' .. str:sub(21)<br />
ti:set_text(str_format("%s: %s", var.name, str))<br />
end,<br />
BOOL = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_bool, rang)<br />
local val = "false"<br />
if rang:le_uint() > 0 then<br />
val = "true"<br />
end<br />
ti:set_text(str_format("%s: %s", var.name, val))<br />
end,<br />
IPADDR = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add(fds.var_ipaddr, rang)<br />
ti:set_text(str_format("%s: %s", var.name, tostring(rang:ipv4())))<br />
end,<br />
IPPORT = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add(fds.var_ipport, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:uint()))<br />
end,<br />
}<br />
<br />
-- un-register lludp to handle udp port range<br />
local function unregister_udp_port_range(start_port, end_port)<br />
if not start_port or start_port <= 0 or not end_port or end_port <= 0 then<br />
return<br />
end<br />
udp_port_table = DissectorTable.get("udp.port")<br />
for port = start_port,end_port do<br />
udp_port_table:remove(port,lludp_proto)<br />
end<br />
end<br />
<br />
-- register lludp to handle udp port range<br />
local function register_udp_port_range(start_port, end_port)<br />
if not start_port or start_port <= 0 or not end_port or end_port <= 0 then<br />
return<br />
end<br />
udp_port_table = DissectorTable.get("udp.port")<br />
for port = start_port,end_port do<br />
udp_port_table:add(port,lludp_proto)<br />
end<br />
end<br />
<br />
-- handle preferences changes.<br />
function lludp_proto.init(arg1, arg2)<br />
local old_start, old_end<br />
local new_start, new_end<br />
-- check if preferences have changed.<br />
for pref_name,old_v in pairs(current_settings) do<br />
local new_v = lludp_proto.prefs[pref_name]<br />
if new_v ~= old_v then<br />
if pref_name == "template_file" then<br />
-- load & parse message_template.msg file.<br />
local file = new_v<br />
if file and file:len() > 0 then<br />
local new_details = parse_template(file)<br />
if new_details then<br />
message_details = new_details<br />
end<br />
end<br />
elseif pref_name == "udp_port_start" then<br />
old_start = old_v<br />
new_start = new_v<br />
elseif pref_name == "udp_port_end" then<br />
old_end = old_v<br />
new_end = new_v<br />
end<br />
-- save new value.<br />
current_settings[pref_name] = new_v<br />
end<br />
end<br />
-- un-register old port range<br />
if old_start and old_end then<br />
unregister_udp_port_range(tonumber(old_start), tonumber(old_end))<br />
end<br />
-- register new port range.<br />
if new_start and new_end then<br />
register_udp_port_range(tonumber(new_start), tonumber(new_end))<br />
end<br />
end<br />
<br />
-- parse flag bits.<br />
local FLAG_ZER = 4<br />
local FLAG_REL = 3<br />
local FLAG_RES = 2<br />
local FLAG_ACK = 1<br />
local flag_names = {"ACK", "RES", "REL", "ZER"}<br />
local bits_lookup = {<br />
{},<br />
{1},<br />
{2},<br />
{2,1},<br />
{3},<br />
{3,1},<br />
{3,2},<br />
{3,2,1},<br />
{4},<br />
{4,1},<br />
{4,2},<br />
{4,2,1},<br />
{4,3},<br />
{4,3,1},<br />
{4,3,2},<br />
{4,3,2,1},<br />
}<br />
local function parse_flags(flags)<br />
flags = (flags / 16) + 1<br />
local bit_list = bits_lookup[flags]<br />
local bits = {}<br />
local names = ""<br />
for _, bit in ipairs(bit_list) do<br />
bits[bit] = true<br />
if names:len() > 0 then<br />
names = names .. ", "<br />
end<br />
names = names .. flag_names[bit]<br />
end<br />
return bits, names<br />
end<br />
<br />
local function grow_buff(buff, size)<br />
local old_size = buff:len()<br />
if old_size > size then return end<br />
-- buffer needs to grow<br />
buff:set_size(size)<br />
-- fill new space with zeros<br />
for i = old_size,size-1 do<br />
buff:set_index(i, 0)<br />
end<br />
end<br />
<br />
local function zero_decode(zero_buf)<br />
local out_buf = ByteArray.new()<br />
local zero_off = 0<br />
local zero_len = zero_buf:len()<br />
local out_size = 0<br />
local out_off = 0<br />
local b<br />
-- pre-allocate<br />
grow_buff(out_buf, zero_len)<br />
out_size = zero_len<br />
-- zero expand<br />
repeat<br />
b = zero_buf:get_index(zero_off)<br />
if b == 0 then<br />
-- get zero count<br />
local count = zero_buf:get_index(zero_off + 1)<br />
if count == 0 then count = 255 end<br />
out_off = out_off + count<br />
-- fill zeros<br />
if out_off > out_size then<br />
out_size = out_off + 128<br />
grow_buff(out_buf, out_size)<br />
end<br />
zero_off = zero_off + 2<br />
else<br />
if out_off >= out_size then<br />
out_size = out_off + (zero_len - zero_off) + 4<br />
grow_buff(out_buf, out_size)<br />
end<br />
-- copy non-zero bytes.<br />
out_buf:set_index(out_off,b)<br />
zero_off = zero_off + 1<br />
out_off = out_off + 1<br />
end<br />
until zero_off == zero_len<br />
-- truncate to real size.<br />
out_buf:set_size(out_off)<br />
<br />
return out_buf:tvb("Decompressed Data")<br />
end<br />
<br />
local function parse_msg_id(buff)<br />
local b = buff:get_index(0)<br />
local msg_id = b<br />
local msg_id_len = 1<br />
if b == 255 then<br />
b = buff:get_index(1)<br />
msg_id = msg_id * 256 + b<br />
msg_id_len = 2<br />
if b == 255 then<br />
b = buff:get_index(2)<br />
msg_id = msg_id * 256 + b<br />
b = buff:get_index(3)<br />
msg_id = msg_id * 256 + b<br />
msg_id_len = 4<br />
end<br />
end<br />
return msg_id, msg_id_len<br />
end<br />
<br />
-- get message name.<br />
local function get_msg_name(msg_id)<br />
-- check that we have message details<br />
if message_details == nil then<br />
return str_format("0x%08x", msg_id)<br />
end<br />
-- find message name from id.<br />
local msg = message_details.msgs[msg_id]<br />
-- Invalid message id<br />
if msg == nil then<br />
return str_format("0x%08x", msg_id)<br />
end<br />
return msg.name<br />
end<br />
<br />
-- calculate length a block.<br />
local function get_block_length(msg_buffer, start_offset, block)<br />
-- check if bock is fixed length.<br />
if block.fixed_length then<br />
return block.min_length<br />
end<br />
-- parse block's variables to calculate total block length.<br />
local offset = start_offset<br />
local rang<br />
for _,var in ipairs(block) do<br />
local len = 0<br />
if var.has_count then<br />
-- variable with length bytes.<br />
len = var.count_length<br />
--print(var.name, offset, ", len:", len)<br />
rang = msg_buffer(offset, len)<br />
len = len + rang:le_uint()<br />
--print(var.name, var.count_length, ", total:", len)<br />
else<br />
-- fixed length variable<br />
len = var.length<br />
--print(var.name, ", total:", len)<br />
end<br />
offset = offset + len<br />
end<br />
return (offset - start_offset)<br />
end<br />
<br />
-- build block tree<br />
local function build_block_tree(msg_buffer, block_tree, start_offset, block)<br />
local offset = start_offset<br />
local rang<br />
-- parse block's variables<br />
for _,var in ipairs(block) do<br />
local len = 0<br />
if var.has_count then<br />
-- variable with length bytes.<br />
len = var.count_length<br />
rang = msg_buffer(offset, len)<br />
len = len + rang:le_uint()<br />
else<br />
-- fixed length variable<br />
len = var.length<br />
end<br />
-- get variable's type field.<br />
local handler = variable_handlers[var.type]<br />
-- parse variable.<br />
if handler then<br />
handler(block_tree, msg_buffer, offset, len, var)<br />
end<br />
offset = offset + len<br />
end<br />
return (offset - start_offset)<br />
end<br />
<br />
-- buid message tree<br />
local function build_msg_tree(msg_buffer, msg_tree, msg_id)<br />
local offset = 0<br />
local rang<br />
-- check that we have message details<br />
if message_details == nil then<br />
msg_tree:set_text(str_format("Message Id: 0x%08x", msg_id))<br />
return nil<br />
end<br />
-- find message name from id.<br />
local msg = message_details.msgs[msg_id]<br />
-- Invalid message id<br />
if msg == nil then<br />
msg = str_format("Invalid message id: 0x%08x", msg_id)<br />
msg_tree:add_expert_info(PI_MALFORMED, PI_ERROR, msg)<br />
msg_tree:set_text(msg)<br />
return nil<br />
end<br />
-- skip message id bytes.<br />
offset = msg.id_length<br />
-- set message name.<br />
msg_tree:set_text(msg.name .. ":")<br />
-- proccess message blocks<br />
for _,block in ipairs(msg) do<br />
local count = block.count<br />
if count == nil then<br />
-- parse count byte.<br />
rang = msg_buffer(offset,1)<br />
count = rang:uint()<br />
msg_tree:add(fds.block_count,rang)<br />
offset = offset + 1<br />
end<br />
-- print("block name: ", block.name, count)<br />
for n=1,count do<br />
local block_len = get_block_length(msg_buffer, offset, block)<br />
-- parse block<br />
rang = msg_buffer(offset, block_len)<br />
local block_tree = msg_tree:add(fds.block,rang)<br />
if count > 1 then<br />
block_tree:set_text(str_format("%s: %d of %d",block.name,n,count))<br />
else<br />
block_tree:set_text(block.name)<br />
end<br />
-- parse block variables.<br />
build_block_tree(msg_buffer, block_tree, offset, block)<br />
offset = offset + block_len<br />
end<br />
end<br />
return msg.name<br />
end<br />
<br />
-- packet dissector<br />
function lludp_proto.dissector(buffer,pinfo,tree)<br />
local rang,offset<br />
pinfo.cols.protocol = "LLUDP"<br />
local lludp_tree = tree:add(lludp_proto,buffer(),"Linden UDP Protocol")<br />
-- Flags byte.<br />
offset = 0<br />
rang = buffer(offset,1)<br />
local flags = rang:uint()<br />
local flags_bits, flags_list = parse_flags(flags)<br />
flags_tree = lludp_tree:add(fds.flags, rang)<br />
flags_tree:set_text("Flags: " .. str_format('0x%02X (%s)', flags, flags_list))<br />
flags_tree:add(fds.flags_zero, rang)<br />
flags_tree:add(fds.flags_reliable, rang)<br />
flags_tree:add(fds.flags_resent, rang)<br />
flags_tree:add(fds.flags_ack, rang)<br />
offset = offset + 1<br />
-- Sequence number 4 bytes.<br />
rang = buffer(offset,4)<br />
local sequence = rang:uint()<br />
lludp_tree:add(fds.sequence, rang)<br />
offset = offset + 4<br />
-- Extra header length.<br />
rang = buffer(offset,1)<br />
local extra_length = rang:uint()<br />
lludp_tree:add(fds.extra_len,rang)<br />
offset = offset + 1<br />
-- Extra header data.<br />
if extra_length > 0 then<br />
rang = buffer(offset, extra_length)<br />
lludp_tree:add(fds.extra_bytes, rang)<br />
offset = offset + extra_length<br />
end<br />
-- Appended Acks. count<br />
local acks_bytes = 0<br />
local acks_count = 0<br />
if flags_bits[FLAG_ACK] then<br />
rang = buffer(buffer:len() - 1, 1)<br />
acks_count = rang:uint()<br />
acks_bytes = (acks_count * 4) + 1<br />
end<br />
-- Zero Decode<br />
local msg_len = (buffer:len() - acks_bytes) - offset<br />
if flags_bits[FLAG_ZER] then<br />
msg_buffer=zero_decode(buffer(offset,msg_len):bytes())<br />
msg_len = msg_buffer:len()<br />
offset = 0<br />
else<br />
msg_buffer = buffer(offset, msg_len):tvb()<br />
offset = 0<br />
end<br />
-- Message ID<br />
local msg_id, msg_id_len = -1, 4<br />
if msg_id_len > msg_len then<br />
msg_id_len = msg_len<br />
end<br />
msg_id, msg_id_len = parse_msg_id(msg_buffer(offset, msg_id_len):bytes())<br />
rang = msg_buffer(offset, msg_id_len)<br />
local msg_id_tree = lludp_tree:add(fds.msg_id, rang)<br />
local msg_name = get_msg_name(msg_id)<br />
if msg_name == nil then<br />
msg_id_tree:set_text(str_format("Message name: 0x%08x", msg_id))<br />
else<br />
msg_id_tree:set_text(str_format("Message name: %s",msg_name))<br />
end<br />
-- Message body.<br />
rang = msg_buffer(offset, msg_len)<br />
local msg_tree = lludp_tree:add(fds.msg, rang)<br />
build_msg_tree(msg_buffer, msg_tree, msg_id)<br />
-- Appended Acks. list.<br />
if flags_bits[FLAG_ACK] then<br />
local acks_off = buffer:len()<br />
rang = buffer(acks_off - 1, 1)<br />
acks_off = acks_off - acks_bytes<br />
local acks_tree = lludp_tree:add(fds.acks_count, rang)<br />
for i = 1,acks_count do<br />
rang = buffer(acks_off,4)<br />
acks_tree:add(fds.acks, rang)<br />
acks_off = acks_off + 4<br />
end<br />
end<br />
-- Info column<br />
pinfo.cols.info = str_format('[%s] Seq=%u Type=%s', flags_list, sequence, msg_name)<br />
end<br />
<br />
-- register lludp to handle udp ports 9000-9003<br />
register_udp_port_range(9000,9003)<br />
</source><br />
<br />
=== File "llmessage.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
dofile("lexer.lua")<br />
<br />
local lexer<br />
local cur_token = nil<br />
local cur_token_str = nil<br />
<br />
local function get_token(skip_tokens)<br />
repeat<br />
token = lexer.get_token()<br />
if token ~= nil then<br />
cur_token = token[1]<br />
cur_token_str = token[2]<br />
end<br />
until token == nil or not skip_tokens[cur_token]<br />
return token<br />
end<br />
<br />
local function run_parser(parser)<br />
local state = parser.init()<br />
if parser.skip_tokens == nil then parser.skip_tokens = {} end<br />
while get_token(parser.skip_tokens) do<br />
-- check what the parser is expecting next.<br />
if state.expect then<br />
-- check expected type<br />
if state.expect ~= cur_token then<br />
error(string.format("state.expected token '%s' instead of '%s'",<br />
TokenNames[state.expect], TokenNames[cur_token]))<br />
end<br />
-- reset expect field<br />
state.expect = nil<br />
end<br />
if state.expect_str then<br />
-- check expected string<br />
if state.expect_str ~= cur_token_str then<br />
error(string.format("state.expected token '%s' instead of '%s'",<br />
state.expect_str, cur_token_str))<br />
end<br />
-- reset expect_str field<br />
state.expect_str = nil<br />
end<br />
-- get handler function for current token.<br />
local f = parser[cur_token]<br />
if f ~= nil then<br />
local ret = f(state)<br />
if ret then<br />
-- praser finished.<br />
return ret<br />
end<br />
elseif parser.unhandled_error then<br />
error(string.format("unhandled token '%s' when paring '%s'\n", cur_token_str, parser.name))<br />
end<br />
end<br />
return parser.eof(state)<br />
end<br />
<br />
-- Known variable types and there fixed length.<br />
-- length == -1, requires a number after the type that is the length of count field<br />
-- length == -2, requires a number after the type that is the fixed variable length.<br />
VariableTypes = {<br />
Null = 0,<br />
Fixed = -2,<br />
Variable = -1,<br />
U8 = 1,<br />
U16 = 2,<br />
U32 = 4,<br />
U64 = 8,<br />
S8 = 1,<br />
S16 = 2,<br />
S32 = 4,<br />
S64 = 8,<br />
F32 = 4,<br />
F64 = 8,<br />
LLVector3 = 12,<br />
LLVector3d = 24,<br />
LLVector4 = 16,<br />
LLQuaternion = 12,<br />
LLUUID = 16,<br />
BOOL = 1,<br />
IPADDR = 4,<br />
IPPORT = 2,<br />
}<br />
<br />
--<br />
-- Variable parser<br />
--<br />
local variable_parser = {<br />
name = "variable",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
return {<br />
name = "<MISSING VARIABLE NAME>",<br />
type = "Null",<br />
has_count = false,<br />
length = 0,<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
required = 2<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "type"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "type" then<br />
state.type = cur_token_str<br />
state.length = VariableTypes[state.type]<br />
if state.length == nil then<br />
error("Unknown variable type: " .. cur_token_str)<br />
elseif state.length == -1 or state.length == -2 then<br />
state.expect = Token.NUMBER<br />
else<br />
state.required = state.required - 1<br />
end<br />
else<br />
error(string.format("unhandled variable identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "type" then<br />
if state.length == -1 then<br />
-- variable field length uses embedded count field<br />
state.has_count = true<br />
state.count_length = tonumber(cur_token_str)<br />
state.length = nil<br />
elseif state.length == -2 then<br />
-- fixed field length<br />
state.length = tonumber(cur_token_str)<br />
end<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled variable number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
error("sub block not allowed in variable block")<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of variable: " .. state.name)<br />
end,<br />
}<br />
<br />
-- Block Quantities<br />
local BlockQuantity = {<br />
Single = 1,<br />
Variable = -1,<br />
Multiple = -2,<br />
}<br />
<br />
--<br />
-- Block parser<br />
--<br />
local block_parser = {<br />
name = "block",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
return {<br />
name = "<MISSING BLOCK NAME>",<br />
quantity = "Single",<br />
count = 0,<br />
min_length = 0,<br />
fixed_length = true,<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
required = 2<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "quantity"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "quantity" then<br />
state.quantity = cur_token_str<br />
state.count = BlockQuantity[cur_token_str]<br />
if state.count == nil then<br />
error("Unknown block quantity: " .. cur_token_str)<br />
elseif state.count == -2 then<br />
state.expect_field = "count"<br />
state.expect = Token.NUMBER<br />
else<br />
if state.count == -1 then<br />
state.has_count = true<br />
state.count_length = 1<br />
state.count = nil<br />
end<br />
state.required = state.required - 1<br />
end<br />
else<br />
error(string.format("unhandled block identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "count" then<br />
state.count = tonumber(cur_token_str)<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled block number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local variable = run_parser(variable_parser)<br />
table.insert(state,variable)<br />
-- add length of fixed length variables to minimal length of block.<br />
if variable.has_count then<br />
state.min_length = state.min_length + variable.count_length<br />
state.fixed_length = false<br />
else<br />
state.min_length = state.min_length + variable.length<br />
end<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of block: " .. state.name)<br />
end,<br />
}<br />
<br />
--<br />
-- Message parser<br />
--<br />
local message_parser = {<br />
name = "message",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
-- create state<br />
return {<br />
name = "<MISSING MESSAGE NAME>",<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
fixed_length = true,<br />
min_length = 0,<br />
required = 5<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "frequency"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "frequency" then<br />
state.frequency = cur_token_str<br />
state.expect = Token.NUMBER<br />
state.required = state.required - 1<br />
elseif state.expect_field == "trust" then<br />
state.trust = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "compression"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "compression" then<br />
state.compression = cur_token_str<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled message identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "frequency" then<br />
state.number = tonumber(cur_token_str)<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "trust"<br />
state.required = state.required - 1<br />
-- create true message id from frequency and message number<br />
local freq = state.frequency<br />
if freq == "High" then<br />
-- High is already correct.<br />
state.id = state.number<br />
state.id_length = 1<br />
elseif freq == "Medium" then<br />
state.id = tonumber("0xFF" .. string.format("%02X", state.number))<br />
state.id_length = 2<br />
elseif freq == "Low" then<br />
state.id = tonumber("0xFFFF" .. string.format("%04X", state.number))<br />
state.id_length = 4<br />
else<br />
-- Fixed is already correct.<br />
state.id = state.number<br />
state.id_length = 4<br />
end<br />
else<br />
error(string.format("unhandled message number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local block = run_parser(block_parser)<br />
table.insert(state,block)<br />
-- add min length of block to minimal length of message<br />
local min_length = block.min_length<br />
if block.has_count then<br />
-- add one byte for the block count<br />
min_length = min_length + 1<br />
state.fixed_length = false<br />
else<br />
-- if block is not fixed length then message can't be fixed length.<br />
if not block.fixed_length then<br />
state.fixed_length = false<br />
end<br />
min_length = min_length * block.count<br />
end<br />
state.min_length = state.min_length + min_length<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of message: " .. state.name)<br />
end,<br />
}<br />
<br />
--<br />
-- Template file parser<br />
--<br />
local template_parser = {<br />
name = "message_template",<br />
unhandled_error = false,<br />
init = function()<br />
return {<br />
version = 0,<br />
msg_count = 0,<br />
msgs = {}<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
-- handle version<br />
if cur_token_str == "version" then<br />
state.expect = Token.NUMBER<br />
state.last_ident = cur_token_str<br />
else<br />
error(string.format("unknown template identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
-- handle version number<br />
if state.last_ident == "version" then<br />
state.last_ident = nil<br />
state.version = tonumber(cur_token_str)<br />
-- check version number<br />
if state.version ~= 2 then<br />
error("invalid verion: " .. state.version)<br />
end<br />
else<br />
error(string.format("unhandled template number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local message = run_parser(message_parser)<br />
state.msg_count = state.msg_count + 1<br />
state.msgs[message.id] = message<br />
end,<br />
["}"] = function(state)<br />
error(string.format("unhandled '%s' token",cur_token_str))<br />
end,<br />
eof = function(state)<br />
return state<br />
end,<br />
}<br />
<br />
function parse_template(file)<br />
-- create lexer<br />
local status, ret = pcall(get_lexer,file)<br />
if not status then<br />
ret = string.format("LLUDP: Failed parse file into tokens: %s\n%s\n", file, ret)<br />
error(ret, 0)<br />
return nil<br />
end<br />
lexer = ret<br />
-- parse template file<br />
local status, ret = pcall(run_parser,template_parser)<br />
if not status then<br />
ret = string.format("LLUDP: Failed parsing on line %s:%d: '%s'\n%s\n",<br />
file, lexer.get_line_number(), lexer.get_line(), ret)<br />
error(ret, 0)<br />
return nil<br />
end<br />
io.write("finished parsing: " .. file .. "\n")<br />
-- return list of messages parsed from file.<br />
return ret<br />
end<br />
<br />
--parse_template("message_template.msg")<br />
--print_tokens("message_template.msg")<br />
<br />
</source><br />
<br />
=== File "lexer.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
-- Token types.<br />
Token = {<br />
NONE = -1,<br />
IDENTIFIER = -2,<br />
NUMBER = -3,<br />
COMMENT = -6,<br />
EOL = -7,<br />
["{"] = "{",<br />
["}"] = "}",<br />
}<br />
TokenNames = {<br />
[-1] = "NONE",<br />
[-2] = "IDENTIFIER",<br />
[-3] = "NUMBER",<br />
[-6] = "COMMENT",<br />
[-7] = "EOL",<br />
["{"] = "{",<br />
["}"] = "}",<br />
}<br />
<br />
-- parse line into array of tokens.<br />
local function default_parse_tokens(line)<br />
local tokens = {}<br />
local comment = nil<br />
-- check for a comment on this line.<br />
local idx = line:find("//")<br />
if idx ~= nil then<br />
comment = {Token.COMMENT, line:sub(idx)}<br />
-- remove comment from line<br />
line = line:sub(1,idx - 1)<br />
end<br />
<br />
-- split line into tokens using white-space as token delimitator<br />
for tok in line:gmatch("%s?([^%s]+)") do<br />
local tok_type = Token.NONE<br />
-- check for number<br />
if tonumber(tok) ~= nil then<br />
tok_type = Token.NUMBER<br />
elseif Token[tok] then<br />
-- token is same as type<br />
tok_type = Token[tok]<br />
else<br />
-- token is an identifier<br />
tok_type = Token.IDENTIFIER<br />
end<br />
table.insert(tokens,{tok_type,tok})<br />
end<br />
-- insert comment token.<br />
if comment ~= nil then<br />
table.insert(tokens,comment)<br />
end<br />
-- add token to mark the end of this line<br />
table.insert(tokens,{Token.EOL, ""})<br />
return tokens<br />
end<br />
<br />
function get_lexer(file, parse_tokens)<br />
-- use the default line tokenizer if one is not provided<br />
if parse_tokens == nil then parse_tokens = default_parse_tokens end<br />
-- next/current line code<br />
local line_num = 0<br />
local line = nil<br />
local next_line = io.lines(file)<br />
-- parse line tokens code<br />
local get_next_token = nil<br />
local next_tokens = function ()<br />
local f, tokens, idx<br />
repeat<br />
line_num = line_num + 1<br />
line = next_line()<br />
if line == nil then return nil end<br />
tokens = parse_tokens(line)<br />
until tokens ~= nil<br />
-- create get_next_toekn function from table iterator<br />
f, tokens, idx = ipairs(tokens)<br />
get_next_token = function()<br />
idx, token = f(tokens, idx)<br />
return token<br />
end<br />
return tokens<br />
end<br />
<br />
-- get first group of tokens<br />
if next_tokens() == nil then<br />
-- error reading file or empty file<br />
return nil<br />
end<br />
-- build lexer table.<br />
local lexer = {<br />
get_token = function ()<br />
local token<br />
repeat<br />
token = get_next_token()<br />
if token == nil then<br />
-- get next group of tokens<br />
if next_tokens() == nil then<br />
-- end of file.<br />
return nil<br />
end<br />
end<br />
until token ~= nil<br />
return token<br />
end,<br />
get_line_number = function() return line_num end,<br />
get_line = function() return line end<br />
}<br />
return lexer<br />
end<br />
<br />
function print_tokens(file)<br />
local lexer = get_lexer(file)<br />
local num = -1<br />
while true do<br />
local tok = lexer.get_token()<br />
if tok == nil then<br />
break<br />
end<br />
if num ~= lexer.get_line_number() then<br />
num = lexer.get_line_number()<br />
io.write("\n")<br />
io.write(string.format("%d: ",num))<br />
end<br />
io.write(string.format("%s ",tok[2]))<br />
end<br />
io.write("\n")<br />
end<br />
<br />
</source></div>Aiaustinhttp://opensimulator.org/wiki/LLUDP_DissectorLLUDP Dissector2023-01-14T17:43:56Z<p>Aiaustin: /* LLUDP preferences */ Improve instructions to be up to date.</p>
<hr />
<div>__NOTOC__<br />
{{Quicklinks}}<br />
<br /><br />
<br />
== LLUDP protocol dissector ==<br />
On this page you will find the Lua code for a wireshark protocol dissector that can parse the message_template.msg file and use that information to decode all the message fields from the Linden UDP protocol. The code is also available on [https://github.com/Neopallium/lludp_dissector GitHub]<br />
<br />
== Installing ==<br />
* Requires Wireshark with Lua support. This is enabled by default in recent versions of Wireshark. [http://wiki.wireshark.org/Lua See this page for Wireshark support for Lua]<br />
<br />
=== on Linux ===<br />
* Copy all five source files into ~/.wireshark<br />
* '''Check if correct''' If you need to run Wireshark as the root user or using sudo then you will need to edit the scripts into one file by replacing the dofile("script.lua") calls with the contents of file between the quotes.<br />
* '''Check if correct''' The other method is to add your user account to the correct group (on Gentoo it is group "wireshark") that will allow your non-root user to capture packets.<br />
<br />
=== on Windows ===<br />
* Copy the lludp folder containing the five .lua source files into the Wireshark AppData directory <br />
<br />
'''Windows versions up to Windows 11'''<br />
<br />
C:\Users\<username>\AppData\Roaming\Wireshark\Plugins <br />
<br />
'''XP/2000'''<br />
<br />
C:\users\<username>\AppData\Wireshark<br />
<br />
* init.lua is not required as LUA support is enabled by default in recent versions of Wireshark.<br />
<br />
== LLUDP preferences ==<br />
There are three preferences that can be changed from Wireshark's Preferences - Protocols - LLUDP dialog:<br />
* Message template file: Full path to the message_template.msg file used to decode message name & details from the packets. On Windows use double backslash '\\' instead of single blackslash '\' to separate directories (Example "C:\\Program Files\\FirestormOS-Releasex64\\app_settings\\message_template.msg").<br />
* UDP port range start: First UDP port to mark as LLUDP packets. (default 13000)<br />
* UDP port range end: Last UDP port to mark as LLUDP packets. (default 13050)<br />
<br />
If your OpenSimulator regions are using only ports 9000-9050 then change the UDP port range.<br />
<br />
== Description of source files ==<br />
* "init.lua" -- simple script that loads the "lludp.lua" script.<br />
* "lludp.lua" -- contains the code that decodes each packet header and decompresses zero-encoded packets. This file uses wireshark only functions for accessing packet bytes and building a tree of information from each packet.<br />
* "llmessage.lua" -- contains the message_template.msg file parser the decodes the tokens from the lexer into an tree of tables containing all details about each message/block/variable from the template file. This file only has pure lua code.<br />
* "lexer.lua" -- contains the template file lexer. This lexer knows how to tokenize the template file into the follow tokens: IDENTIFIER, NUMBER, COMMENT, EOL. The stream of tokens produced by this lexer is parsed by the "llmessage.lua" file. This file only has pure lua code.<br />
<br />
=== Code license ===<br />
This Wireshark dissector maybe used under the terms of the "Simplified BSD License" or the [http://www.gnu.org/licenses/gpl.txt GPL]. -- Robert G. Jakabosky <bobby@sharedrealm.com><br />
<pre><br />
Simplified BSD License:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.<br />
<br />
</pre><br />
<br />
=== File "init.lua" ===<br />
<source lang="lua"><br />
<br />
-- register http to handle tcp ports 8000-8010 and 9000<br />
do<br />
local tcp_port_table = DissectorTable.get("tcp.port")<br />
local http_dissector = tcp_port_table:get_dissector(80)<br />
for port = 8000,8010 do<br />
tcp_port_table:add(port,http_dissector)<br />
end<br />
tcp_port_table:add(9000,http_dissector)<br />
end<br />
<br />
-- Load lludp protocol dissector.<br />
dofile("lludp.lua")<br />
<br />
</source><br />
<br />
=== File "lludp.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
dofile("llmessage.lua")<br />
<br />
-- cache globals to local for speed.<br />
local str_format=string.format<br />
<br />
-- test if ByteArray only has printable ASCII character and ends with '\0'<br />
local allowed_special = {<br />
[0] = true, -- null<br />
[9] = true, -- tab<br />
[10] = true, -- new line<br />
[13] = true, -- carriage return<br />
}<br />
local function is_string(bytes)<br />
local c<br />
local max = bytes:len() - 1<br />
for i=0,max do<br />
c = bytes:get_index(i)<br />
if c >= 127 then -- not ascii character<br />
return false<br />
elseif c < 32 then -- control character range<br />
if not allowed_special[c] then<br />
-- control characters between NULL and Space<br />
return false<br />
elseif c == 0 and i < max then<br />
-- null byte only allowed at end.<br />
return false<br />
end<br />
end<br />
end<br />
return true<br />
end<br />
<br />
-- lludp protocol example<br />
-- declare our protocol<br />
local lludp_proto = Proto("lludp","LLUDP","LindenLabs UDP Protocol")<br />
<br />
-- setup preferences<br />
lludp_proto.prefs["template_file"] =<br />
Pref.string("Message template file", "message_template.msg", "Message template file")<br />
lludp_proto.prefs["udp_port_start"] =<br />
Pref.string("UDP port range start", "13000", "First UDP port to decode as this protocol")<br />
lludp_proto.prefs["udp_port_end"] =<br />
Pref.string("UDP port range end", "13050", "Last UDP port to decode as this protocol")<br />
-- current preferences settings.<br />
local current_settings = {<br />
template_file = "",<br />
udp_port_start = -1,<br />
udp_port_end = -1,<br />
}<br />
-- current list of parsed messages.<br />
local message_details = nil<br />
<br />
-- setup protocol fields.<br />
lludp_proto.fields = {}<br />
local fds = lludp_proto.fields<br />
fds.flags = ProtoField.uint8("lludp.flags", "Flags", base.HEX, nil, 0xFF)<br />
fds.flags_zero = ProtoField.uint8("lludp.flags.zero", "Zero", base.HEX, nil, 0x80)<br />
fds.flags_reliable = ProtoField.uint8("lludp.flags.rel", "Reliable", base.HEX, nil, 0x40)<br />
fds.flags_resent = ProtoField.uint8("lludp.flags.res", "Resent", base.HEX, nil, 0x20)<br />
fds.flags_ack = ProtoField.uint8("lludp.flags.ack", "Ack", base.HEX, nil, 0x10)<br />
fds.sequence = ProtoField.uint32("lludp.sequence", "Sequence", base.DEC)<br />
fds.extra_len = ProtoField.uint8("lludp.extra_len", "Extra length", base.DEC)<br />
fds.extra_bytes = ProtoField.bytes("lludp.extra_bytes", "Extra header", base.HEX)<br />
fds.msg_id = ProtoField.uint32("lludp.msg.id", "Message ID", base.HEX)<br />
fds.msg_name = ProtoField.bytes("lludp.msg.name", "Message name")<br />
fds.msg = ProtoField.bytes("lludp.msg", "Message body", base.HEX)<br />
fds.acks_count = ProtoField.uint8("lludp.acks_count", "Acks count", base.DEC)<br />
fds.acks = ProtoField.uint32("lludp.acks", "Acks", base.DEC)<br />
fds.block_count = ProtoField.uint8("lludp.block_count", "Block count", base.DEC)<br />
fds.block = ProtoField.bytes("lludp.block", "Block", base.HEX)<br />
fds.var_fixed = ProtoField.bytes("lludp.var.fixed", "Fixed blob", base.HEX)<br />
fds.var_variable = ProtoField.bytes("lludp.var.variable", "Variable blob", base.HEX)<br />
fds.var_string = ProtoField.stringz("lludp.var.string", "String")<br />
fds.var_u8 = ProtoField.uint8("lludp.var.u8", "U8", base.DEC)<br />
fds.var_u16 = ProtoField.uint16("lludp.var.u16", "U16", base.DEC)<br />
fds.var_u32 = ProtoField.uint32("lludp.var.u32", "U32", base.DEC)<br />
fds.var_u64 = ProtoField.uint64("lludp.var.u64", "U64", base.DEC)<br />
fds.var_s8 = ProtoField.int8("lludp.var.s8", "S8", base.DEC)<br />
fds.var_s16 = ProtoField.int16("lludp.var.s16", "S16", base.DEC)<br />
fds.var_s32 = ProtoField.int32("lludp.var.s32", "S32", base.DEC)<br />
fds.var_s64 = ProtoField.int64("lludp.var.s64", "S64", base.DEC)<br />
fds.var_f32 = ProtoField.float("lludp.var.f32", "F32", base.DEC)<br />
fds.var_f64 = ProtoField.double("lludp.var.f64", "F64", base.DEC)<br />
fds.var_llvector3 = ProtoField.bytes("lludp.var.llvector3", "LLVector3", base.HEX)<br />
fds.var_llvector3d = ProtoField.bytes("lludp.var.llvector3d", "LLVector3d", base.HEX)<br />
fds.var_llvector4 = ProtoField.bytes("lludp.var.llvector4", "LLVector4", base.HEX)<br />
fds.var_llquaternion = ProtoField.bytes("lludp.var.llquaternion", "LLQuaternion", base.HEX)<br />
fds.var_lluuid = ProtoField.bytes("lludp.var.lluuid", "LLUUID", base.HEX)<br />
fds.var_bool = ProtoField.uint8("lludp.var.bool", "BOOL", base.DEC)<br />
fds.var_ipaddr = ProtoField.ipv4("lludp.var.ipaddr", "IPADDR", base.DEC)<br />
fds.var_ipport = ProtoField.uint16("lludp.var.ipport", "IPPORT", base.DEC)<br />
-- variable type handlers.<br />
local variable_handlers = {<br />
Fixed = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_fixed, rang)<br />
if len <= 4 then<br />
ti:set_text(str_format("%s: 0x%08x",var.name, rang:uint()))<br />
else<br />
ti:set_text(str_format("%s: length=%d, Blob:%s", var.name, len, tostring(rang)))<br />
end<br />
end,<br />
Variable = function(block_tree, buffer, offset, len, var)<br />
local is_data = false<br />
-- try to guess if this field is text.<br />
if var.name:find("Data") then<br />
-- this is a data find.<br />
is_data = true<br />
end<br />
local str_rang = buffer(offset + var.count_length, len - var.count_length)<br />
local bytes = str_rang:bytes()<br />
if not is_data and is_string(bytes) then<br />
local str = str_rang:string()<br />
local ti = block_tree:add(fds.var_string, buffer(offset, len))<br />
ti:set_text(str_format("%s: %s", var.name, str))<br />
else<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_variable, rang)<br />
if len <= 4 then<br />
ti:set_text(str_format("%s: 0x%08x",var.name, rang:uint()))<br />
else<br />
ti:set_text(str_format("%s: length=%d, Blob:%s", var.name, len, tostring(rang)))<br />
end<br />
end<br />
end,<br />
U8 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u8, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U16 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u16, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u32, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u64, rang)<br />
ti:set_text(str_format("%s: 0x%s",var.name, tostring(rang)))<br />
end,<br />
S8 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s8, rang)<br />
local num = rang:le_uint()<br />
if num > 127 then num = num - 256 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S16 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s16, rang)<br />
local num = rang:le_uint()<br />
if num > 32768 then num = num - 65536 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s32, rang)<br />
local num = rang:le_uint()<br />
if num > 2147483648 then num = num - 4294967296 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s64, rang)<br />
ti:set_text(str_format("%s: 0x%s",var.name, tostring(rang)))<br />
end,<br />
F32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_f32, rang)<br />
ti:set_text(str_format("%s: %f", var.name, rang:le_float()))<br />
end,<br />
F64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_f64, rang)<br />
ti:set_text(str_format("%s: %f", var.name, rang:le_float()))<br />
end,<br />
LLVector3 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector3, rang)<br />
-- parse LLVector3<br />
local x,y,z<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f>", var.name, x, y, z))<br />
end,<br />
LLVector3d = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector3d, rang)<br />
-- parse LLVector3d<br />
local x,y,z<br />
x = buffer(offset + 0,8):le_float()<br />
y = buffer(offset + 8,8):le_float()<br />
z = buffer(offset + 16,8):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f>", var.name, x, y, z))<br />
end,<br />
LLVector4 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector4, rang)<br />
-- parse LLVector4<br />
local x,y,z,s<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
s = buffer(offset + 12,4):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f,%f>", var.name, x, y, z, s))<br />
end,<br />
LLQuaternion = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llquaternion, rang)<br />
-- parse LLQuaternion<br />
local x,y,z,w<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
-- calculate W<br />
w = 1 - (x * x) - (y * y) - (z * z)<br />
if w > 0 then<br />
w = math.sqrt(w)<br />
else<br />
w = 0<br />
end<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f,%f>", var.name, x, y, z, w))<br />
end,<br />
LLUUID = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_lluuid, rang)<br />
local str = tostring(rang)<br />
str = str:sub(1,8) .. '-' ..<br />
str:sub(9,12) .. '-' .. str:sub(13,16) .. '-' ..<br />
str:sub(17,20) .. '-' .. str:sub(21)<br />
ti:set_text(str_format("%s: %s", var.name, str))<br />
end,<br />
BOOL = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_bool, rang)<br />
local val = "false"<br />
if rang:le_uint() > 0 then<br />
val = "true"<br />
end<br />
ti:set_text(str_format("%s: %s", var.name, val))<br />
end,<br />
IPADDR = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add(fds.var_ipaddr, rang)<br />
ti:set_text(str_format("%s: %s", var.name, tostring(rang:ipv4())))<br />
end,<br />
IPPORT = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add(fds.var_ipport, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:uint()))<br />
end,<br />
}<br />
<br />
-- un-register lludp to handle udp port range<br />
local function unregister_udp_port_range(start_port, end_port)<br />
if not start_port or start_port <= 0 or not end_port or end_port <= 0 then<br />
return<br />
end<br />
udp_port_table = DissectorTable.get("udp.port")<br />
for port = start_port,end_port do<br />
udp_port_table:remove(port,lludp_proto)<br />
end<br />
end<br />
<br />
-- register lludp to handle udp port range<br />
local function register_udp_port_range(start_port, end_port)<br />
if not start_port or start_port <= 0 or not end_port or end_port <= 0 then<br />
return<br />
end<br />
udp_port_table = DissectorTable.get("udp.port")<br />
for port = start_port,end_port do<br />
udp_port_table:add(port,lludp_proto)<br />
end<br />
end<br />
<br />
-- handle preferences changes.<br />
function lludp_proto.init(arg1, arg2)<br />
local old_start, old_end<br />
local new_start, new_end<br />
-- check if preferences have changed.<br />
for pref_name,old_v in pairs(current_settings) do<br />
local new_v = lludp_proto.prefs[pref_name]<br />
if new_v ~= old_v then<br />
if pref_name == "template_file" then<br />
-- load & parse message_template.msg file.<br />
local file = new_v<br />
if file and file:len() > 0 then<br />
local new_details = parse_template(file)<br />
if new_details then<br />
message_details = new_details<br />
end<br />
end<br />
elseif pref_name == "udp_port_start" then<br />
old_start = old_v<br />
new_start = new_v<br />
elseif pref_name == "udp_port_end" then<br />
old_end = old_v<br />
new_end = new_v<br />
end<br />
-- save new value.<br />
current_settings[pref_name] = new_v<br />
end<br />
end<br />
-- un-register old port range<br />
if old_start and old_end then<br />
unregister_udp_port_range(tonumber(old_start), tonumber(old_end))<br />
end<br />
-- register new port range.<br />
if new_start and new_end then<br />
register_udp_port_range(tonumber(new_start), tonumber(new_end))<br />
end<br />
end<br />
<br />
-- parse flag bits.<br />
local FLAG_ZER = 4<br />
local FLAG_REL = 3<br />
local FLAG_RES = 2<br />
local FLAG_ACK = 1<br />
local flag_names = {"ACK", "RES", "REL", "ZER"}<br />
local bits_lookup = {<br />
{},<br />
{1},<br />
{2},<br />
{2,1},<br />
{3},<br />
{3,1},<br />
{3,2},<br />
{3,2,1},<br />
{4},<br />
{4,1},<br />
{4,2},<br />
{4,2,1},<br />
{4,3},<br />
{4,3,1},<br />
{4,3,2},<br />
{4,3,2,1},<br />
}<br />
local function parse_flags(flags)<br />
flags = (flags / 16) + 1<br />
local bit_list = bits_lookup[flags]<br />
local bits = {}<br />
local names = ""<br />
for _, bit in ipairs(bit_list) do<br />
bits[bit] = true<br />
if names:len() > 0 then<br />
names = names .. ", "<br />
end<br />
names = names .. flag_names[bit]<br />
end<br />
return bits, names<br />
end<br />
<br />
local function grow_buff(buff, size)<br />
local old_size = buff:len()<br />
if old_size > size then return end<br />
-- buffer needs to grow<br />
buff:set_size(size)<br />
-- fill new space with zeros<br />
for i = old_size,size-1 do<br />
buff:set_index(i, 0)<br />
end<br />
end<br />
<br />
local function zero_decode(zero_buf)<br />
local out_buf = ByteArray.new()<br />
local zero_off = 0<br />
local zero_len = zero_buf:len()<br />
local out_size = 0<br />
local out_off = 0<br />
local b<br />
-- pre-allocate<br />
grow_buff(out_buf, zero_len)<br />
out_size = zero_len<br />
-- zero expand<br />
repeat<br />
b = zero_buf:get_index(zero_off)<br />
if b == 0 then<br />
-- get zero count<br />
local count = zero_buf:get_index(zero_off + 1)<br />
if count == 0 then count = 255 end<br />
out_off = out_off + count<br />
-- fill zeros<br />
if out_off > out_size then<br />
out_size = out_off + 128<br />
grow_buff(out_buf, out_size)<br />
end<br />
zero_off = zero_off + 2<br />
else<br />
if out_off >= out_size then<br />
out_size = out_off + (zero_len - zero_off) + 4<br />
grow_buff(out_buf, out_size)<br />
end<br />
-- copy non-zero bytes.<br />
out_buf:set_index(out_off,b)<br />
zero_off = zero_off + 1<br />
out_off = out_off + 1<br />
end<br />
until zero_off == zero_len<br />
-- truncate to real size.<br />
out_buf:set_size(out_off)<br />
<br />
return out_buf:tvb("Decompressed Data")<br />
end<br />
<br />
local function parse_msg_id(buff)<br />
local b = buff:get_index(0)<br />
local msg_id = b<br />
local msg_id_len = 1<br />
if b == 255 then<br />
b = buff:get_index(1)<br />
msg_id = msg_id * 256 + b<br />
msg_id_len = 2<br />
if b == 255 then<br />
b = buff:get_index(2)<br />
msg_id = msg_id * 256 + b<br />
b = buff:get_index(3)<br />
msg_id = msg_id * 256 + b<br />
msg_id_len = 4<br />
end<br />
end<br />
return msg_id, msg_id_len<br />
end<br />
<br />
-- get message name.<br />
local function get_msg_name(msg_id)<br />
-- check that we have message details<br />
if message_details == nil then<br />
return str_format("0x%08x", msg_id)<br />
end<br />
-- find message name from id.<br />
local msg = message_details.msgs[msg_id]<br />
-- Invalid message id<br />
if msg == nil then<br />
return str_format("0x%08x", msg_id)<br />
end<br />
return msg.name<br />
end<br />
<br />
-- calculate length a block.<br />
local function get_block_length(msg_buffer, start_offset, block)<br />
-- check if bock is fixed length.<br />
if block.fixed_length then<br />
return block.min_length<br />
end<br />
-- parse block's variables to calculate total block length.<br />
local offset = start_offset<br />
local rang<br />
for _,var in ipairs(block) do<br />
local len = 0<br />
if var.has_count then<br />
-- variable with length bytes.<br />
len = var.count_length<br />
--print(var.name, offset, ", len:", len)<br />
rang = msg_buffer(offset, len)<br />
len = len + rang:le_uint()<br />
--print(var.name, var.count_length, ", total:", len)<br />
else<br />
-- fixed length variable<br />
len = var.length<br />
--print(var.name, ", total:", len)<br />
end<br />
offset = offset + len<br />
end<br />
return (offset - start_offset)<br />
end<br />
<br />
-- build block tree<br />
local function build_block_tree(msg_buffer, block_tree, start_offset, block)<br />
local offset = start_offset<br />
local rang<br />
-- parse block's variables<br />
for _,var in ipairs(block) do<br />
local len = 0<br />
if var.has_count then<br />
-- variable with length bytes.<br />
len = var.count_length<br />
rang = msg_buffer(offset, len)<br />
len = len + rang:le_uint()<br />
else<br />
-- fixed length variable<br />
len = var.length<br />
end<br />
-- get variable's type field.<br />
local handler = variable_handlers[var.type]<br />
-- parse variable.<br />
if handler then<br />
handler(block_tree, msg_buffer, offset, len, var)<br />
end<br />
offset = offset + len<br />
end<br />
return (offset - start_offset)<br />
end<br />
<br />
-- buid message tree<br />
local function build_msg_tree(msg_buffer, msg_tree, msg_id)<br />
local offset = 0<br />
local rang<br />
-- check that we have message details<br />
if message_details == nil then<br />
msg_tree:set_text(str_format("Message Id: 0x%08x", msg_id))<br />
return nil<br />
end<br />
-- find message name from id.<br />
local msg = message_details.msgs[msg_id]<br />
-- Invalid message id<br />
if msg == nil then<br />
msg = str_format("Invalid message id: 0x%08x", msg_id)<br />
msg_tree:add_expert_info(PI_MALFORMED, PI_ERROR, msg)<br />
msg_tree:set_text(msg)<br />
return nil<br />
end<br />
-- skip message id bytes.<br />
offset = msg.id_length<br />
-- set message name.<br />
msg_tree:set_text(msg.name .. ":")<br />
-- proccess message blocks<br />
for _,block in ipairs(msg) do<br />
local count = block.count<br />
if count == nil then<br />
-- parse count byte.<br />
rang = msg_buffer(offset,1)<br />
count = rang:uint()<br />
msg_tree:add(fds.block_count,rang)<br />
offset = offset + 1<br />
end<br />
-- print("block name: ", block.name, count)<br />
for n=1,count do<br />
local block_len = get_block_length(msg_buffer, offset, block)<br />
-- parse block<br />
rang = msg_buffer(offset, block_len)<br />
local block_tree = msg_tree:add(fds.block,rang)<br />
if count > 1 then<br />
block_tree:set_text(str_format("%s: %d of %d",block.name,n,count))<br />
else<br />
block_tree:set_text(block.name)<br />
end<br />
-- parse block variables.<br />
build_block_tree(msg_buffer, block_tree, offset, block)<br />
offset = offset + block_len<br />
end<br />
end<br />
return msg.name<br />
end<br />
<br />
-- packet dissector<br />
function lludp_proto.dissector(buffer,pinfo,tree)<br />
local rang,offset<br />
pinfo.cols.protocol = "LLUDP"<br />
local lludp_tree = tree:add(lludp_proto,buffer(),"Linden UDP Protocol")<br />
-- Flags byte.<br />
offset = 0<br />
rang = buffer(offset,1)<br />
local flags = rang:uint()<br />
local flags_bits, flags_list = parse_flags(flags)<br />
flags_tree = lludp_tree:add(fds.flags, rang)<br />
flags_tree:set_text("Flags: " .. str_format('0x%02X (%s)', flags, flags_list))<br />
flags_tree:add(fds.flags_zero, rang)<br />
flags_tree:add(fds.flags_reliable, rang)<br />
flags_tree:add(fds.flags_resent, rang)<br />
flags_tree:add(fds.flags_ack, rang)<br />
offset = offset + 1<br />
-- Sequence number 4 bytes.<br />
rang = buffer(offset,4)<br />
local sequence = rang:uint()<br />
lludp_tree:add(fds.sequence, rang)<br />
offset = offset + 4<br />
-- Extra header length.<br />
rang = buffer(offset,1)<br />
local extra_length = rang:uint()<br />
lludp_tree:add(fds.extra_len,rang)<br />
offset = offset + 1<br />
-- Extra header data.<br />
if extra_length > 0 then<br />
rang = buffer(offset, extra_length)<br />
lludp_tree:add(fds.extra_bytes, rang)<br />
offset = offset + extra_length<br />
end<br />
-- Appended Acks. count<br />
local acks_bytes = 0<br />
local acks_count = 0<br />
if flags_bits[FLAG_ACK] then<br />
rang = buffer(buffer:len() - 1, 1)<br />
acks_count = rang:uint()<br />
acks_bytes = (acks_count * 4) + 1<br />
end<br />
-- Zero Decode<br />
local msg_len = (buffer:len() - acks_bytes) - offset<br />
if flags_bits[FLAG_ZER] then<br />
msg_buffer=zero_decode(buffer(offset,msg_len):bytes())<br />
msg_len = msg_buffer:len()<br />
offset = 0<br />
else<br />
msg_buffer = buffer(offset, msg_len):tvb()<br />
offset = 0<br />
end<br />
-- Message ID<br />
local msg_id, msg_id_len = -1, 4<br />
if msg_id_len > msg_len then<br />
msg_id_len = msg_len<br />
end<br />
msg_id, msg_id_len = parse_msg_id(msg_buffer(offset, msg_id_len):bytes())<br />
rang = msg_buffer(offset, msg_id_len)<br />
local msg_id_tree = lludp_tree:add(fds.msg_id, rang)<br />
local msg_name = get_msg_name(msg_id)<br />
if msg_name == nil then<br />
msg_id_tree:set_text(str_format("Message name: 0x%08x", msg_id))<br />
else<br />
msg_id_tree:set_text(str_format("Message name: %s",msg_name))<br />
end<br />
-- Message body.<br />
rang = msg_buffer(offset, msg_len)<br />
local msg_tree = lludp_tree:add(fds.msg, rang)<br />
build_msg_tree(msg_buffer, msg_tree, msg_id)<br />
-- Appended Acks. list.<br />
if flags_bits[FLAG_ACK] then<br />
local acks_off = buffer:len()<br />
rang = buffer(acks_off - 1, 1)<br />
acks_off = acks_off - acks_bytes<br />
local acks_tree = lludp_tree:add(fds.acks_count, rang)<br />
for i = 1,acks_count do<br />
rang = buffer(acks_off,4)<br />
acks_tree:add(fds.acks, rang)<br />
acks_off = acks_off + 4<br />
end<br />
end<br />
-- Info column<br />
pinfo.cols.info = str_format('[%s] Seq=%u Type=%s', flags_list, sequence, msg_name)<br />
end<br />
<br />
-- register lludp to handle udp ports 9000-9003<br />
register_udp_port_range(9000,9003)<br />
</source><br />
<br />
=== File "llmessage.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
dofile("lexer.lua")<br />
<br />
local lexer<br />
local cur_token = nil<br />
local cur_token_str = nil<br />
<br />
local function get_token(skip_tokens)<br />
repeat<br />
token = lexer.get_token()<br />
if token ~= nil then<br />
cur_token = token[1]<br />
cur_token_str = token[2]<br />
end<br />
until token == nil or not skip_tokens[cur_token]<br />
return token<br />
end<br />
<br />
local function run_parser(parser)<br />
local state = parser.init()<br />
if parser.skip_tokens == nil then parser.skip_tokens = {} end<br />
while get_token(parser.skip_tokens) do<br />
-- check what the parser is expecting next.<br />
if state.expect then<br />
-- check expected type<br />
if state.expect ~= cur_token then<br />
error(string.format("state.expected token '%s' instead of '%s'",<br />
TokenNames[state.expect], TokenNames[cur_token]))<br />
end<br />
-- reset expect field<br />
state.expect = nil<br />
end<br />
if state.expect_str then<br />
-- check expected string<br />
if state.expect_str ~= cur_token_str then<br />
error(string.format("state.expected token '%s' instead of '%s'",<br />
state.expect_str, cur_token_str))<br />
end<br />
-- reset expect_str field<br />
state.expect_str = nil<br />
end<br />
-- get handler function for current token.<br />
local f = parser[cur_token]<br />
if f ~= nil then<br />
local ret = f(state)<br />
if ret then<br />
-- praser finished.<br />
return ret<br />
end<br />
elseif parser.unhandled_error then<br />
error(string.format("unhandled token '%s' when paring '%s'\n", cur_token_str, parser.name))<br />
end<br />
end<br />
return parser.eof(state)<br />
end<br />
<br />
-- Known variable types and there fixed length.<br />
-- length == -1, requires a number after the type that is the length of count field<br />
-- length == -2, requires a number after the type that is the fixed variable length.<br />
VariableTypes = {<br />
Null = 0,<br />
Fixed = -2,<br />
Variable = -1,<br />
U8 = 1,<br />
U16 = 2,<br />
U32 = 4,<br />
U64 = 8,<br />
S8 = 1,<br />
S16 = 2,<br />
S32 = 4,<br />
S64 = 8,<br />
F32 = 4,<br />
F64 = 8,<br />
LLVector3 = 12,<br />
LLVector3d = 24,<br />
LLVector4 = 16,<br />
LLQuaternion = 12,<br />
LLUUID = 16,<br />
BOOL = 1,<br />
IPADDR = 4,<br />
IPPORT = 2,<br />
}<br />
<br />
--<br />
-- Variable parser<br />
--<br />
local variable_parser = {<br />
name = "variable",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
return {<br />
name = "<MISSING VARIABLE NAME>",<br />
type = "Null",<br />
has_count = false,<br />
length = 0,<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
required = 2<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "type"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "type" then<br />
state.type = cur_token_str<br />
state.length = VariableTypes[state.type]<br />
if state.length == nil then<br />
error("Unknown variable type: " .. cur_token_str)<br />
elseif state.length == -1 or state.length == -2 then<br />
state.expect = Token.NUMBER<br />
else<br />
state.required = state.required - 1<br />
end<br />
else<br />
error(string.format("unhandled variable identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "type" then<br />
if state.length == -1 then<br />
-- variable field length uses embedded count field<br />
state.has_count = true<br />
state.count_length = tonumber(cur_token_str)<br />
state.length = nil<br />
elseif state.length == -2 then<br />
-- fixed field length<br />
state.length = tonumber(cur_token_str)<br />
end<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled variable number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
error("sub block not allowed in variable block")<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of variable: " .. state.name)<br />
end,<br />
}<br />
<br />
-- Block Quantities<br />
local BlockQuantity = {<br />
Single = 1,<br />
Variable = -1,<br />
Multiple = -2,<br />
}<br />
<br />
--<br />
-- Block parser<br />
--<br />
local block_parser = {<br />
name = "block",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
return {<br />
name = "<MISSING BLOCK NAME>",<br />
quantity = "Single",<br />
count = 0,<br />
min_length = 0,<br />
fixed_length = true,<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
required = 2<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "quantity"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "quantity" then<br />
state.quantity = cur_token_str<br />
state.count = BlockQuantity[cur_token_str]<br />
if state.count == nil then<br />
error("Unknown block quantity: " .. cur_token_str)<br />
elseif state.count == -2 then<br />
state.expect_field = "count"<br />
state.expect = Token.NUMBER<br />
else<br />
if state.count == -1 then<br />
state.has_count = true<br />
state.count_length = 1<br />
state.count = nil<br />
end<br />
state.required = state.required - 1<br />
end<br />
else<br />
error(string.format("unhandled block identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "count" then<br />
state.count = tonumber(cur_token_str)<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled block number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local variable = run_parser(variable_parser)<br />
table.insert(state,variable)<br />
-- add length of fixed length variables to minimal length of block.<br />
if variable.has_count then<br />
state.min_length = state.min_length + variable.count_length<br />
state.fixed_length = false<br />
else<br />
state.min_length = state.min_length + variable.length<br />
end<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of block: " .. state.name)<br />
end,<br />
}<br />
<br />
--<br />
-- Message parser<br />
--<br />
local message_parser = {<br />
name = "message",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
-- create state<br />
return {<br />
name = "<MISSING MESSAGE NAME>",<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
fixed_length = true,<br />
min_length = 0,<br />
required = 5<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "frequency"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "frequency" then<br />
state.frequency = cur_token_str<br />
state.expect = Token.NUMBER<br />
state.required = state.required - 1<br />
elseif state.expect_field == "trust" then<br />
state.trust = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "compression"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "compression" then<br />
state.compression = cur_token_str<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled message identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "frequency" then<br />
state.number = tonumber(cur_token_str)<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "trust"<br />
state.required = state.required - 1<br />
-- create true message id from frequency and message number<br />
local freq = state.frequency<br />
if freq == "High" then<br />
-- High is already correct.<br />
state.id = state.number<br />
state.id_length = 1<br />
elseif freq == "Medium" then<br />
state.id = tonumber("0xFF" .. string.format("%02X", state.number))<br />
state.id_length = 2<br />
elseif freq == "Low" then<br />
state.id = tonumber("0xFFFF" .. string.format("%04X", state.number))<br />
state.id_length = 4<br />
else<br />
-- Fixed is already correct.<br />
state.id = state.number<br />
state.id_length = 4<br />
end<br />
else<br />
error(string.format("unhandled message number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local block = run_parser(block_parser)<br />
table.insert(state,block)<br />
-- add min length of block to minimal length of message<br />
local min_length = block.min_length<br />
if block.has_count then<br />
-- add one byte for the block count<br />
min_length = min_length + 1<br />
state.fixed_length = false<br />
else<br />
-- if block is not fixed length then message can't be fixed length.<br />
if not block.fixed_length then<br />
state.fixed_length = false<br />
end<br />
min_length = min_length * block.count<br />
end<br />
state.min_length = state.min_length + min_length<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of message: " .. state.name)<br />
end,<br />
}<br />
<br />
--<br />
-- Template file parser<br />
--<br />
local template_parser = {<br />
name = "message_template",<br />
unhandled_error = false,<br />
init = function()<br />
return {<br />
version = 0,<br />
msg_count = 0,<br />
msgs = {}<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
-- handle version<br />
if cur_token_str == "version" then<br />
state.expect = Token.NUMBER<br />
state.last_ident = cur_token_str<br />
else<br />
error(string.format("unknown template identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
-- handle version number<br />
if state.last_ident == "version" then<br />
state.last_ident = nil<br />
state.version = tonumber(cur_token_str)<br />
-- check version number<br />
if state.version ~= 2 then<br />
error("invalid verion: " .. state.version)<br />
end<br />
else<br />
error(string.format("unhandled template number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local message = run_parser(message_parser)<br />
state.msg_count = state.msg_count + 1<br />
state.msgs[message.id] = message<br />
end,<br />
["}"] = function(state)<br />
error(string.format("unhandled '%s' token",cur_token_str))<br />
end,<br />
eof = function(state)<br />
return state<br />
end,<br />
}<br />
<br />
function parse_template(file)<br />
-- create lexer<br />
local status, ret = pcall(get_lexer,file)<br />
if not status then<br />
ret = string.format("LLUDP: Failed parse file into tokens: %s\n%s\n", file, ret)<br />
error(ret, 0)<br />
return nil<br />
end<br />
lexer = ret<br />
-- parse template file<br />
local status, ret = pcall(run_parser,template_parser)<br />
if not status then<br />
ret = string.format("LLUDP: Failed parsing on line %s:%d: '%s'\n%s\n",<br />
file, lexer.get_line_number(), lexer.get_line(), ret)<br />
error(ret, 0)<br />
return nil<br />
end<br />
io.write("finished parsing: " .. file .. "\n")<br />
-- return list of messages parsed from file.<br />
return ret<br />
end<br />
<br />
--parse_template("message_template.msg")<br />
--print_tokens("message_template.msg")<br />
<br />
</source><br />
<br />
=== File "lexer.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
-- Token types.<br />
Token = {<br />
NONE = -1,<br />
IDENTIFIER = -2,<br />
NUMBER = -3,<br />
COMMENT = -6,<br />
EOL = -7,<br />
["{"] = "{",<br />
["}"] = "}",<br />
}<br />
TokenNames = {<br />
[-1] = "NONE",<br />
[-2] = "IDENTIFIER",<br />
[-3] = "NUMBER",<br />
[-6] = "COMMENT",<br />
[-7] = "EOL",<br />
["{"] = "{",<br />
["}"] = "}",<br />
}<br />
<br />
-- parse line into array of tokens.<br />
local function default_parse_tokens(line)<br />
local tokens = {}<br />
local comment = nil<br />
-- check for a comment on this line.<br />
local idx = line:find("//")<br />
if idx ~= nil then<br />
comment = {Token.COMMENT, line:sub(idx)}<br />
-- remove comment from line<br />
line = line:sub(1,idx - 1)<br />
end<br />
<br />
-- split line into tokens using white-space as token delimitator<br />
for tok in line:gmatch("%s?([^%s]+)") do<br />
local tok_type = Token.NONE<br />
-- check for number<br />
if tonumber(tok) ~= nil then<br />
tok_type = Token.NUMBER<br />
elseif Token[tok] then<br />
-- token is same as type<br />
tok_type = Token[tok]<br />
else<br />
-- token is an identifier<br />
tok_type = Token.IDENTIFIER<br />
end<br />
table.insert(tokens,{tok_type,tok})<br />
end<br />
-- insert comment token.<br />
if comment ~= nil then<br />
table.insert(tokens,comment)<br />
end<br />
-- add token to mark the end of this line<br />
table.insert(tokens,{Token.EOL, ""})<br />
return tokens<br />
end<br />
<br />
function get_lexer(file, parse_tokens)<br />
-- use the default line tokenizer if one is not provided<br />
if parse_tokens == nil then parse_tokens = default_parse_tokens end<br />
-- next/current line code<br />
local line_num = 0<br />
local line = nil<br />
local next_line = io.lines(file)<br />
-- parse line tokens code<br />
local get_next_token = nil<br />
local next_tokens = function ()<br />
local f, tokens, idx<br />
repeat<br />
line_num = line_num + 1<br />
line = next_line()<br />
if line == nil then return nil end<br />
tokens = parse_tokens(line)<br />
until tokens ~= nil<br />
-- create get_next_toekn function from table iterator<br />
f, tokens, idx = ipairs(tokens)<br />
get_next_token = function()<br />
idx, token = f(tokens, idx)<br />
return token<br />
end<br />
return tokens<br />
end<br />
<br />
-- get first group of tokens<br />
if next_tokens() == nil then<br />
-- error reading file or empty file<br />
return nil<br />
end<br />
-- build lexer table.<br />
local lexer = {<br />
get_token = function ()<br />
local token<br />
repeat<br />
token = get_next_token()<br />
if token == nil then<br />
-- get next group of tokens<br />
if next_tokens() == nil then<br />
-- end of file.<br />
return nil<br />
end<br />
end<br />
until token ~= nil<br />
return token<br />
end,<br />
get_line_number = function() return line_num end,<br />
get_line = function() return line end<br />
}<br />
return lexer<br />
end<br />
<br />
function print_tokens(file)<br />
local lexer = get_lexer(file)<br />
local num = -1<br />
while true do<br />
local tok = lexer.get_token()<br />
if tok == nil then<br />
break<br />
end<br />
if num ~= lexer.get_line_number() then<br />
num = lexer.get_line_number()<br />
io.write("\n")<br />
io.write(string.format("%d: ",num))<br />
end<br />
io.write(string.format("%s ",tok[2]))<br />
end<br />
io.write("\n")<br />
end<br />
<br />
</source></div>Aiaustinhttp://opensimulator.org/wiki/LLUDP_DissectorLLUDP Dissector2023-01-14T17:41:33Z<p>Aiaustin: /* Installing */ Check needed as to whether Linux details are correct</p>
<hr />
<div>__NOTOC__<br />
{{Quicklinks}}<br />
<br /><br />
<br />
== LLUDP protocol dissector ==<br />
On this page you will find the Lua code for a wireshark protocol dissector that can parse the message_template.msg file and use that information to decode all the message fields from the Linden UDP protocol. The code is also available on [https://github.com/Neopallium/lludp_dissector GitHub]<br />
<br />
== Installing ==<br />
* Requires Wireshark with Lua support. This is enabled by default in recent versions of Wireshark. [http://wiki.wireshark.org/Lua See this page for Wireshark support for Lua]<br />
<br />
=== on Linux ===<br />
* Copy all five source files into ~/.wireshark<br />
* '''Check if correct''' If you need to run Wireshark as the root user or using sudo then you will need to edit the scripts into one file by replacing the dofile("script.lua") calls with the contents of file between the quotes.<br />
* '''Check if correct''' The other method is to add your user account to the correct group (on Gentoo it is group "wireshark") that will allow your non-root user to capture packets.<br />
<br />
=== on Windows ===<br />
* Copy the lludp folder containing the five .lua source files into the Wireshark AppData directory <br />
<br />
'''Windows versions up to Windows 11'''<br />
<br />
C:\Users\<username>\AppData\Roaming\Wireshark\Plugins <br />
<br />
'''XP/2000'''<br />
<br />
C:\users\<username>\AppData\Wireshark<br />
<br />
* init.lua is not required as LUA support is enabled by default in recent versions of Wireshark.<br />
<br />
== LLUDP preferences ==<br />
There are three preferences that can be changed from wiresharks "Preferences" dialog:<br />
* Message template file: Full path to the message_template.msg file used to decode message name & details from the packets. On windows XP/Vista use double backslash '\\' instead of single blackslash '\' to separate directories (Example "C:\\Program Files\\SecondLife\\app_settings\\message_template.msg").<br />
* UDP port range start: First UDP port to mark as LLUDP packets. (default 13000)<br />
* UDP port range end: Last UDP port to mark as LLUDP packets. (default 13050)<br />
<br />
If your OpenSimulator regions are using ports 9000-9050 range then change the UDP port range.<br />
<br />
== Description of source files ==<br />
* "init.lua" -- simple script that loads the "lludp.lua" script.<br />
* "lludp.lua" -- contains the code that decodes each packet header and decompresses zero-encoded packets. This file uses wireshark only functions for accessing packet bytes and building a tree of information from each packet.<br />
* "llmessage.lua" -- contains the message_template.msg file parser the decodes the tokens from the lexer into an tree of tables containing all details about each message/block/variable from the template file. This file only has pure lua code.<br />
* "lexer.lua" -- contains the template file lexer. This lexer knows how to tokenize the template file into the follow tokens: IDENTIFIER, NUMBER, COMMENT, EOL. The stream of tokens produced by this lexer is parsed by the "llmessage.lua" file. This file only has pure lua code.<br />
<br />
=== Code license ===<br />
This Wireshark dissector maybe used under the terms of the "Simplified BSD License" or the [http://www.gnu.org/licenses/gpl.txt GPL]. -- Robert G. Jakabosky <bobby@sharedrealm.com><br />
<pre><br />
Simplified BSD License:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.<br />
<br />
</pre><br />
<br />
=== File "init.lua" ===<br />
<source lang="lua"><br />
<br />
-- register http to handle tcp ports 8000-8010 and 9000<br />
do<br />
local tcp_port_table = DissectorTable.get("tcp.port")<br />
local http_dissector = tcp_port_table:get_dissector(80)<br />
for port = 8000,8010 do<br />
tcp_port_table:add(port,http_dissector)<br />
end<br />
tcp_port_table:add(9000,http_dissector)<br />
end<br />
<br />
-- Load lludp protocol dissector.<br />
dofile("lludp.lua")<br />
<br />
</source><br />
<br />
=== File "lludp.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
dofile("llmessage.lua")<br />
<br />
-- cache globals to local for speed.<br />
local str_format=string.format<br />
<br />
-- test if ByteArray only has printable ASCII character and ends with '\0'<br />
local allowed_special = {<br />
[0] = true, -- null<br />
[9] = true, -- tab<br />
[10] = true, -- new line<br />
[13] = true, -- carriage return<br />
}<br />
local function is_string(bytes)<br />
local c<br />
local max = bytes:len() - 1<br />
for i=0,max do<br />
c = bytes:get_index(i)<br />
if c >= 127 then -- not ascii character<br />
return false<br />
elseif c < 32 then -- control character range<br />
if not allowed_special[c] then<br />
-- control characters between NULL and Space<br />
return false<br />
elseif c == 0 and i < max then<br />
-- null byte only allowed at end.<br />
return false<br />
end<br />
end<br />
end<br />
return true<br />
end<br />
<br />
-- lludp protocol example<br />
-- declare our protocol<br />
local lludp_proto = Proto("lludp","LLUDP","LindenLabs UDP Protocol")<br />
<br />
-- setup preferences<br />
lludp_proto.prefs["template_file"] =<br />
Pref.string("Message template file", "message_template.msg", "Message template file")<br />
lludp_proto.prefs["udp_port_start"] =<br />
Pref.string("UDP port range start", "13000", "First UDP port to decode as this protocol")<br />
lludp_proto.prefs["udp_port_end"] =<br />
Pref.string("UDP port range end", "13050", "Last UDP port to decode as this protocol")<br />
-- current preferences settings.<br />
local current_settings = {<br />
template_file = "",<br />
udp_port_start = -1,<br />
udp_port_end = -1,<br />
}<br />
-- current list of parsed messages.<br />
local message_details = nil<br />
<br />
-- setup protocol fields.<br />
lludp_proto.fields = {}<br />
local fds = lludp_proto.fields<br />
fds.flags = ProtoField.uint8("lludp.flags", "Flags", base.HEX, nil, 0xFF)<br />
fds.flags_zero = ProtoField.uint8("lludp.flags.zero", "Zero", base.HEX, nil, 0x80)<br />
fds.flags_reliable = ProtoField.uint8("lludp.flags.rel", "Reliable", base.HEX, nil, 0x40)<br />
fds.flags_resent = ProtoField.uint8("lludp.flags.res", "Resent", base.HEX, nil, 0x20)<br />
fds.flags_ack = ProtoField.uint8("lludp.flags.ack", "Ack", base.HEX, nil, 0x10)<br />
fds.sequence = ProtoField.uint32("lludp.sequence", "Sequence", base.DEC)<br />
fds.extra_len = ProtoField.uint8("lludp.extra_len", "Extra length", base.DEC)<br />
fds.extra_bytes = ProtoField.bytes("lludp.extra_bytes", "Extra header", base.HEX)<br />
fds.msg_id = ProtoField.uint32("lludp.msg.id", "Message ID", base.HEX)<br />
fds.msg_name = ProtoField.bytes("lludp.msg.name", "Message name")<br />
fds.msg = ProtoField.bytes("lludp.msg", "Message body", base.HEX)<br />
fds.acks_count = ProtoField.uint8("lludp.acks_count", "Acks count", base.DEC)<br />
fds.acks = ProtoField.uint32("lludp.acks", "Acks", base.DEC)<br />
fds.block_count = ProtoField.uint8("lludp.block_count", "Block count", base.DEC)<br />
fds.block = ProtoField.bytes("lludp.block", "Block", base.HEX)<br />
fds.var_fixed = ProtoField.bytes("lludp.var.fixed", "Fixed blob", base.HEX)<br />
fds.var_variable = ProtoField.bytes("lludp.var.variable", "Variable blob", base.HEX)<br />
fds.var_string = ProtoField.stringz("lludp.var.string", "String")<br />
fds.var_u8 = ProtoField.uint8("lludp.var.u8", "U8", base.DEC)<br />
fds.var_u16 = ProtoField.uint16("lludp.var.u16", "U16", base.DEC)<br />
fds.var_u32 = ProtoField.uint32("lludp.var.u32", "U32", base.DEC)<br />
fds.var_u64 = ProtoField.uint64("lludp.var.u64", "U64", base.DEC)<br />
fds.var_s8 = ProtoField.int8("lludp.var.s8", "S8", base.DEC)<br />
fds.var_s16 = ProtoField.int16("lludp.var.s16", "S16", base.DEC)<br />
fds.var_s32 = ProtoField.int32("lludp.var.s32", "S32", base.DEC)<br />
fds.var_s64 = ProtoField.int64("lludp.var.s64", "S64", base.DEC)<br />
fds.var_f32 = ProtoField.float("lludp.var.f32", "F32", base.DEC)<br />
fds.var_f64 = ProtoField.double("lludp.var.f64", "F64", base.DEC)<br />
fds.var_llvector3 = ProtoField.bytes("lludp.var.llvector3", "LLVector3", base.HEX)<br />
fds.var_llvector3d = ProtoField.bytes("lludp.var.llvector3d", "LLVector3d", base.HEX)<br />
fds.var_llvector4 = ProtoField.bytes("lludp.var.llvector4", "LLVector4", base.HEX)<br />
fds.var_llquaternion = ProtoField.bytes("lludp.var.llquaternion", "LLQuaternion", base.HEX)<br />
fds.var_lluuid = ProtoField.bytes("lludp.var.lluuid", "LLUUID", base.HEX)<br />
fds.var_bool = ProtoField.uint8("lludp.var.bool", "BOOL", base.DEC)<br />
fds.var_ipaddr = ProtoField.ipv4("lludp.var.ipaddr", "IPADDR", base.DEC)<br />
fds.var_ipport = ProtoField.uint16("lludp.var.ipport", "IPPORT", base.DEC)<br />
-- variable type handlers.<br />
local variable_handlers = {<br />
Fixed = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_fixed, rang)<br />
if len <= 4 then<br />
ti:set_text(str_format("%s: 0x%08x",var.name, rang:uint()))<br />
else<br />
ti:set_text(str_format("%s: length=%d, Blob:%s", var.name, len, tostring(rang)))<br />
end<br />
end,<br />
Variable = function(block_tree, buffer, offset, len, var)<br />
local is_data = false<br />
-- try to guess if this field is text.<br />
if var.name:find("Data") then<br />
-- this is a data find.<br />
is_data = true<br />
end<br />
local str_rang = buffer(offset + var.count_length, len - var.count_length)<br />
local bytes = str_rang:bytes()<br />
if not is_data and is_string(bytes) then<br />
local str = str_rang:string()<br />
local ti = block_tree:add(fds.var_string, buffer(offset, len))<br />
ti:set_text(str_format("%s: %s", var.name, str))<br />
else<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_variable, rang)<br />
if len <= 4 then<br />
ti:set_text(str_format("%s: 0x%08x",var.name, rang:uint()))<br />
else<br />
ti:set_text(str_format("%s: length=%d, Blob:%s", var.name, len, tostring(rang)))<br />
end<br />
end<br />
end,<br />
U8 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u8, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U16 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u16, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u32, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u64, rang)<br />
ti:set_text(str_format("%s: 0x%s",var.name, tostring(rang)))<br />
end,<br />
S8 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s8, rang)<br />
local num = rang:le_uint()<br />
if num > 127 then num = num - 256 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S16 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s16, rang)<br />
local num = rang:le_uint()<br />
if num > 32768 then num = num - 65536 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s32, rang)<br />
local num = rang:le_uint()<br />
if num > 2147483648 then num = num - 4294967296 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s64, rang)<br />
ti:set_text(str_format("%s: 0x%s",var.name, tostring(rang)))<br />
end,<br />
F32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_f32, rang)<br />
ti:set_text(str_format("%s: %f", var.name, rang:le_float()))<br />
end,<br />
F64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_f64, rang)<br />
ti:set_text(str_format("%s: %f", var.name, rang:le_float()))<br />
end,<br />
LLVector3 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector3, rang)<br />
-- parse LLVector3<br />
local x,y,z<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f>", var.name, x, y, z))<br />
end,<br />
LLVector3d = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector3d, rang)<br />
-- parse LLVector3d<br />
local x,y,z<br />
x = buffer(offset + 0,8):le_float()<br />
y = buffer(offset + 8,8):le_float()<br />
z = buffer(offset + 16,8):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f>", var.name, x, y, z))<br />
end,<br />
LLVector4 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector4, rang)<br />
-- parse LLVector4<br />
local x,y,z,s<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
s = buffer(offset + 12,4):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f,%f>", var.name, x, y, z, s))<br />
end,<br />
LLQuaternion = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llquaternion, rang)<br />
-- parse LLQuaternion<br />
local x,y,z,w<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
-- calculate W<br />
w = 1 - (x * x) - (y * y) - (z * z)<br />
if w > 0 then<br />
w = math.sqrt(w)<br />
else<br />
w = 0<br />
end<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f,%f>", var.name, x, y, z, w))<br />
end,<br />
LLUUID = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_lluuid, rang)<br />
local str = tostring(rang)<br />
str = str:sub(1,8) .. '-' ..<br />
str:sub(9,12) .. '-' .. str:sub(13,16) .. '-' ..<br />
str:sub(17,20) .. '-' .. str:sub(21)<br />
ti:set_text(str_format("%s: %s", var.name, str))<br />
end,<br />
BOOL = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_bool, rang)<br />
local val = "false"<br />
if rang:le_uint() > 0 then<br />
val = "true"<br />
end<br />
ti:set_text(str_format("%s: %s", var.name, val))<br />
end,<br />
IPADDR = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add(fds.var_ipaddr, rang)<br />
ti:set_text(str_format("%s: %s", var.name, tostring(rang:ipv4())))<br />
end,<br />
IPPORT = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add(fds.var_ipport, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:uint()))<br />
end,<br />
}<br />
<br />
-- un-register lludp to handle udp port range<br />
local function unregister_udp_port_range(start_port, end_port)<br />
if not start_port or start_port <= 0 or not end_port or end_port <= 0 then<br />
return<br />
end<br />
udp_port_table = DissectorTable.get("udp.port")<br />
for port = start_port,end_port do<br />
udp_port_table:remove(port,lludp_proto)<br />
end<br />
end<br />
<br />
-- register lludp to handle udp port range<br />
local function register_udp_port_range(start_port, end_port)<br />
if not start_port or start_port <= 0 or not end_port or end_port <= 0 then<br />
return<br />
end<br />
udp_port_table = DissectorTable.get("udp.port")<br />
for port = start_port,end_port do<br />
udp_port_table:add(port,lludp_proto)<br />
end<br />
end<br />
<br />
-- handle preferences changes.<br />
function lludp_proto.init(arg1, arg2)<br />
local old_start, old_end<br />
local new_start, new_end<br />
-- check if preferences have changed.<br />
for pref_name,old_v in pairs(current_settings) do<br />
local new_v = lludp_proto.prefs[pref_name]<br />
if new_v ~= old_v then<br />
if pref_name == "template_file" then<br />
-- load & parse message_template.msg file.<br />
local file = new_v<br />
if file and file:len() > 0 then<br />
local new_details = parse_template(file)<br />
if new_details then<br />
message_details = new_details<br />
end<br />
end<br />
elseif pref_name == "udp_port_start" then<br />
old_start = old_v<br />
new_start = new_v<br />
elseif pref_name == "udp_port_end" then<br />
old_end = old_v<br />
new_end = new_v<br />
end<br />
-- save new value.<br />
current_settings[pref_name] = new_v<br />
end<br />
end<br />
-- un-register old port range<br />
if old_start and old_end then<br />
unregister_udp_port_range(tonumber(old_start), tonumber(old_end))<br />
end<br />
-- register new port range.<br />
if new_start and new_end then<br />
register_udp_port_range(tonumber(new_start), tonumber(new_end))<br />
end<br />
end<br />
<br />
-- parse flag bits.<br />
local FLAG_ZER = 4<br />
local FLAG_REL = 3<br />
local FLAG_RES = 2<br />
local FLAG_ACK = 1<br />
local flag_names = {"ACK", "RES", "REL", "ZER"}<br />
local bits_lookup = {<br />
{},<br />
{1},<br />
{2},<br />
{2,1},<br />
{3},<br />
{3,1},<br />
{3,2},<br />
{3,2,1},<br />
{4},<br />
{4,1},<br />
{4,2},<br />
{4,2,1},<br />
{4,3},<br />
{4,3,1},<br />
{4,3,2},<br />
{4,3,2,1},<br />
}<br />
local function parse_flags(flags)<br />
flags = (flags / 16) + 1<br />
local bit_list = bits_lookup[flags]<br />
local bits = {}<br />
local names = ""<br />
for _, bit in ipairs(bit_list) do<br />
bits[bit] = true<br />
if names:len() > 0 then<br />
names = names .. ", "<br />
end<br />
names = names .. flag_names[bit]<br />
end<br />
return bits, names<br />
end<br />
<br />
local function grow_buff(buff, size)<br />
local old_size = buff:len()<br />
if old_size > size then return end<br />
-- buffer needs to grow<br />
buff:set_size(size)<br />
-- fill new space with zeros<br />
for i = old_size,size-1 do<br />
buff:set_index(i, 0)<br />
end<br />
end<br />
<br />
local function zero_decode(zero_buf)<br />
local out_buf = ByteArray.new()<br />
local zero_off = 0<br />
local zero_len = zero_buf:len()<br />
local out_size = 0<br />
local out_off = 0<br />
local b<br />
-- pre-allocate<br />
grow_buff(out_buf, zero_len)<br />
out_size = zero_len<br />
-- zero expand<br />
repeat<br />
b = zero_buf:get_index(zero_off)<br />
if b == 0 then<br />
-- get zero count<br />
local count = zero_buf:get_index(zero_off + 1)<br />
if count == 0 then count = 255 end<br />
out_off = out_off + count<br />
-- fill zeros<br />
if out_off > out_size then<br />
out_size = out_off + 128<br />
grow_buff(out_buf, out_size)<br />
end<br />
zero_off = zero_off + 2<br />
else<br />
if out_off >= out_size then<br />
out_size = out_off + (zero_len - zero_off) + 4<br />
grow_buff(out_buf, out_size)<br />
end<br />
-- copy non-zero bytes.<br />
out_buf:set_index(out_off,b)<br />
zero_off = zero_off + 1<br />
out_off = out_off + 1<br />
end<br />
until zero_off == zero_len<br />
-- truncate to real size.<br />
out_buf:set_size(out_off)<br />
<br />
return out_buf:tvb("Decompressed Data")<br />
end<br />
<br />
local function parse_msg_id(buff)<br />
local b = buff:get_index(0)<br />
local msg_id = b<br />
local msg_id_len = 1<br />
if b == 255 then<br />
b = buff:get_index(1)<br />
msg_id = msg_id * 256 + b<br />
msg_id_len = 2<br />
if b == 255 then<br />
b = buff:get_index(2)<br />
msg_id = msg_id * 256 + b<br />
b = buff:get_index(3)<br />
msg_id = msg_id * 256 + b<br />
msg_id_len = 4<br />
end<br />
end<br />
return msg_id, msg_id_len<br />
end<br />
<br />
-- get message name.<br />
local function get_msg_name(msg_id)<br />
-- check that we have message details<br />
if message_details == nil then<br />
return str_format("0x%08x", msg_id)<br />
end<br />
-- find message name from id.<br />
local msg = message_details.msgs[msg_id]<br />
-- Invalid message id<br />
if msg == nil then<br />
return str_format("0x%08x", msg_id)<br />
end<br />
return msg.name<br />
end<br />
<br />
-- calculate length a block.<br />
local function get_block_length(msg_buffer, start_offset, block)<br />
-- check if bock is fixed length.<br />
if block.fixed_length then<br />
return block.min_length<br />
end<br />
-- parse block's variables to calculate total block length.<br />
local offset = start_offset<br />
local rang<br />
for _,var in ipairs(block) do<br />
local len = 0<br />
if var.has_count then<br />
-- variable with length bytes.<br />
len = var.count_length<br />
--print(var.name, offset, ", len:", len)<br />
rang = msg_buffer(offset, len)<br />
len = len + rang:le_uint()<br />
--print(var.name, var.count_length, ", total:", len)<br />
else<br />
-- fixed length variable<br />
len = var.length<br />
--print(var.name, ", total:", len)<br />
end<br />
offset = offset + len<br />
end<br />
return (offset - start_offset)<br />
end<br />
<br />
-- build block tree<br />
local function build_block_tree(msg_buffer, block_tree, start_offset, block)<br />
local offset = start_offset<br />
local rang<br />
-- parse block's variables<br />
for _,var in ipairs(block) do<br />
local len = 0<br />
if var.has_count then<br />
-- variable with length bytes.<br />
len = var.count_length<br />
rang = msg_buffer(offset, len)<br />
len = len + rang:le_uint()<br />
else<br />
-- fixed length variable<br />
len = var.length<br />
end<br />
-- get variable's type field.<br />
local handler = variable_handlers[var.type]<br />
-- parse variable.<br />
if handler then<br />
handler(block_tree, msg_buffer, offset, len, var)<br />
end<br />
offset = offset + len<br />
end<br />
return (offset - start_offset)<br />
end<br />
<br />
-- buid message tree<br />
local function build_msg_tree(msg_buffer, msg_tree, msg_id)<br />
local offset = 0<br />
local rang<br />
-- check that we have message details<br />
if message_details == nil then<br />
msg_tree:set_text(str_format("Message Id: 0x%08x", msg_id))<br />
return nil<br />
end<br />
-- find message name from id.<br />
local msg = message_details.msgs[msg_id]<br />
-- Invalid message id<br />
if msg == nil then<br />
msg = str_format("Invalid message id: 0x%08x", msg_id)<br />
msg_tree:add_expert_info(PI_MALFORMED, PI_ERROR, msg)<br />
msg_tree:set_text(msg)<br />
return nil<br />
end<br />
-- skip message id bytes.<br />
offset = msg.id_length<br />
-- set message name.<br />
msg_tree:set_text(msg.name .. ":")<br />
-- proccess message blocks<br />
for _,block in ipairs(msg) do<br />
local count = block.count<br />
if count == nil then<br />
-- parse count byte.<br />
rang = msg_buffer(offset,1)<br />
count = rang:uint()<br />
msg_tree:add(fds.block_count,rang)<br />
offset = offset + 1<br />
end<br />
-- print("block name: ", block.name, count)<br />
for n=1,count do<br />
local block_len = get_block_length(msg_buffer, offset, block)<br />
-- parse block<br />
rang = msg_buffer(offset, block_len)<br />
local block_tree = msg_tree:add(fds.block,rang)<br />
if count > 1 then<br />
block_tree:set_text(str_format("%s: %d of %d",block.name,n,count))<br />
else<br />
block_tree:set_text(block.name)<br />
end<br />
-- parse block variables.<br />
build_block_tree(msg_buffer, block_tree, offset, block)<br />
offset = offset + block_len<br />
end<br />
end<br />
return msg.name<br />
end<br />
<br />
-- packet dissector<br />
function lludp_proto.dissector(buffer,pinfo,tree)<br />
local rang,offset<br />
pinfo.cols.protocol = "LLUDP"<br />
local lludp_tree = tree:add(lludp_proto,buffer(),"Linden UDP Protocol")<br />
-- Flags byte.<br />
offset = 0<br />
rang = buffer(offset,1)<br />
local flags = rang:uint()<br />
local flags_bits, flags_list = parse_flags(flags)<br />
flags_tree = lludp_tree:add(fds.flags, rang)<br />
flags_tree:set_text("Flags: " .. str_format('0x%02X (%s)', flags, flags_list))<br />
flags_tree:add(fds.flags_zero, rang)<br />
flags_tree:add(fds.flags_reliable, rang)<br />
flags_tree:add(fds.flags_resent, rang)<br />
flags_tree:add(fds.flags_ack, rang)<br />
offset = offset + 1<br />
-- Sequence number 4 bytes.<br />
rang = buffer(offset,4)<br />
local sequence = rang:uint()<br />
lludp_tree:add(fds.sequence, rang)<br />
offset = offset + 4<br />
-- Extra header length.<br />
rang = buffer(offset,1)<br />
local extra_length = rang:uint()<br />
lludp_tree:add(fds.extra_len,rang)<br />
offset = offset + 1<br />
-- Extra header data.<br />
if extra_length > 0 then<br />
rang = buffer(offset, extra_length)<br />
lludp_tree:add(fds.extra_bytes, rang)<br />
offset = offset + extra_length<br />
end<br />
-- Appended Acks. count<br />
local acks_bytes = 0<br />
local acks_count = 0<br />
if flags_bits[FLAG_ACK] then<br />
rang = buffer(buffer:len() - 1, 1)<br />
acks_count = rang:uint()<br />
acks_bytes = (acks_count * 4) + 1<br />
end<br />
-- Zero Decode<br />
local msg_len = (buffer:len() - acks_bytes) - offset<br />
if flags_bits[FLAG_ZER] then<br />
msg_buffer=zero_decode(buffer(offset,msg_len):bytes())<br />
msg_len = msg_buffer:len()<br />
offset = 0<br />
else<br />
msg_buffer = buffer(offset, msg_len):tvb()<br />
offset = 0<br />
end<br />
-- Message ID<br />
local msg_id, msg_id_len = -1, 4<br />
if msg_id_len > msg_len then<br />
msg_id_len = msg_len<br />
end<br />
msg_id, msg_id_len = parse_msg_id(msg_buffer(offset, msg_id_len):bytes())<br />
rang = msg_buffer(offset, msg_id_len)<br />
local msg_id_tree = lludp_tree:add(fds.msg_id, rang)<br />
local msg_name = get_msg_name(msg_id)<br />
if msg_name == nil then<br />
msg_id_tree:set_text(str_format("Message name: 0x%08x", msg_id))<br />
else<br />
msg_id_tree:set_text(str_format("Message name: %s",msg_name))<br />
end<br />
-- Message body.<br />
rang = msg_buffer(offset, msg_len)<br />
local msg_tree = lludp_tree:add(fds.msg, rang)<br />
build_msg_tree(msg_buffer, msg_tree, msg_id)<br />
-- Appended Acks. list.<br />
if flags_bits[FLAG_ACK] then<br />
local acks_off = buffer:len()<br />
rang = buffer(acks_off - 1, 1)<br />
acks_off = acks_off - acks_bytes<br />
local acks_tree = lludp_tree:add(fds.acks_count, rang)<br />
for i = 1,acks_count do<br />
rang = buffer(acks_off,4)<br />
acks_tree:add(fds.acks, rang)<br />
acks_off = acks_off + 4<br />
end<br />
end<br />
-- Info column<br />
pinfo.cols.info = str_format('[%s] Seq=%u Type=%s', flags_list, sequence, msg_name)<br />
end<br />
<br />
-- register lludp to handle udp ports 9000-9003<br />
register_udp_port_range(9000,9003)<br />
</source><br />
<br />
=== File "llmessage.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
dofile("lexer.lua")<br />
<br />
local lexer<br />
local cur_token = nil<br />
local cur_token_str = nil<br />
<br />
local function get_token(skip_tokens)<br />
repeat<br />
token = lexer.get_token()<br />
if token ~= nil then<br />
cur_token = token[1]<br />
cur_token_str = token[2]<br />
end<br />
until token == nil or not skip_tokens[cur_token]<br />
return token<br />
end<br />
<br />
local function run_parser(parser)<br />
local state = parser.init()<br />
if parser.skip_tokens == nil then parser.skip_tokens = {} end<br />
while get_token(parser.skip_tokens) do<br />
-- check what the parser is expecting next.<br />
if state.expect then<br />
-- check expected type<br />
if state.expect ~= cur_token then<br />
error(string.format("state.expected token '%s' instead of '%s'",<br />
TokenNames[state.expect], TokenNames[cur_token]))<br />
end<br />
-- reset expect field<br />
state.expect = nil<br />
end<br />
if state.expect_str then<br />
-- check expected string<br />
if state.expect_str ~= cur_token_str then<br />
error(string.format("state.expected token '%s' instead of '%s'",<br />
state.expect_str, cur_token_str))<br />
end<br />
-- reset expect_str field<br />
state.expect_str = nil<br />
end<br />
-- get handler function for current token.<br />
local f = parser[cur_token]<br />
if f ~= nil then<br />
local ret = f(state)<br />
if ret then<br />
-- praser finished.<br />
return ret<br />
end<br />
elseif parser.unhandled_error then<br />
error(string.format("unhandled token '%s' when paring '%s'\n", cur_token_str, parser.name))<br />
end<br />
end<br />
return parser.eof(state)<br />
end<br />
<br />
-- Known variable types and there fixed length.<br />
-- length == -1, requires a number after the type that is the length of count field<br />
-- length == -2, requires a number after the type that is the fixed variable length.<br />
VariableTypes = {<br />
Null = 0,<br />
Fixed = -2,<br />
Variable = -1,<br />
U8 = 1,<br />
U16 = 2,<br />
U32 = 4,<br />
U64 = 8,<br />
S8 = 1,<br />
S16 = 2,<br />
S32 = 4,<br />
S64 = 8,<br />
F32 = 4,<br />
F64 = 8,<br />
LLVector3 = 12,<br />
LLVector3d = 24,<br />
LLVector4 = 16,<br />
LLQuaternion = 12,<br />
LLUUID = 16,<br />
BOOL = 1,<br />
IPADDR = 4,<br />
IPPORT = 2,<br />
}<br />
<br />
--<br />
-- Variable parser<br />
--<br />
local variable_parser = {<br />
name = "variable",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
return {<br />
name = "<MISSING VARIABLE NAME>",<br />
type = "Null",<br />
has_count = false,<br />
length = 0,<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
required = 2<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "type"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "type" then<br />
state.type = cur_token_str<br />
state.length = VariableTypes[state.type]<br />
if state.length == nil then<br />
error("Unknown variable type: " .. cur_token_str)<br />
elseif state.length == -1 or state.length == -2 then<br />
state.expect = Token.NUMBER<br />
else<br />
state.required = state.required - 1<br />
end<br />
else<br />
error(string.format("unhandled variable identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "type" then<br />
if state.length == -1 then<br />
-- variable field length uses embedded count field<br />
state.has_count = true<br />
state.count_length = tonumber(cur_token_str)<br />
state.length = nil<br />
elseif state.length == -2 then<br />
-- fixed field length<br />
state.length = tonumber(cur_token_str)<br />
end<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled variable number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
error("sub block not allowed in variable block")<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of variable: " .. state.name)<br />
end,<br />
}<br />
<br />
-- Block Quantities<br />
local BlockQuantity = {<br />
Single = 1,<br />
Variable = -1,<br />
Multiple = -2,<br />
}<br />
<br />
--<br />
-- Block parser<br />
--<br />
local block_parser = {<br />
name = "block",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
return {<br />
name = "<MISSING BLOCK NAME>",<br />
quantity = "Single",<br />
count = 0,<br />
min_length = 0,<br />
fixed_length = true,<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
required = 2<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "quantity"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "quantity" then<br />
state.quantity = cur_token_str<br />
state.count = BlockQuantity[cur_token_str]<br />
if state.count == nil then<br />
error("Unknown block quantity: " .. cur_token_str)<br />
elseif state.count == -2 then<br />
state.expect_field = "count"<br />
state.expect = Token.NUMBER<br />
else<br />
if state.count == -1 then<br />
state.has_count = true<br />
state.count_length = 1<br />
state.count = nil<br />
end<br />
state.required = state.required - 1<br />
end<br />
else<br />
error(string.format("unhandled block identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "count" then<br />
state.count = tonumber(cur_token_str)<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled block number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local variable = run_parser(variable_parser)<br />
table.insert(state,variable)<br />
-- add length of fixed length variables to minimal length of block.<br />
if variable.has_count then<br />
state.min_length = state.min_length + variable.count_length<br />
state.fixed_length = false<br />
else<br />
state.min_length = state.min_length + variable.length<br />
end<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of block: " .. state.name)<br />
end,<br />
}<br />
<br />
--<br />
-- Message parser<br />
--<br />
local message_parser = {<br />
name = "message",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
-- create state<br />
return {<br />
name = "<MISSING MESSAGE NAME>",<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
fixed_length = true,<br />
min_length = 0,<br />
required = 5<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "frequency"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "frequency" then<br />
state.frequency = cur_token_str<br />
state.expect = Token.NUMBER<br />
state.required = state.required - 1<br />
elseif state.expect_field == "trust" then<br />
state.trust = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "compression"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "compression" then<br />
state.compression = cur_token_str<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled message identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "frequency" then<br />
state.number = tonumber(cur_token_str)<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "trust"<br />
state.required = state.required - 1<br />
-- create true message id from frequency and message number<br />
local freq = state.frequency<br />
if freq == "High" then<br />
-- High is already correct.<br />
state.id = state.number<br />
state.id_length = 1<br />
elseif freq == "Medium" then<br />
state.id = tonumber("0xFF" .. string.format("%02X", state.number))<br />
state.id_length = 2<br />
elseif freq == "Low" then<br />
state.id = tonumber("0xFFFF" .. string.format("%04X", state.number))<br />
state.id_length = 4<br />
else<br />
-- Fixed is already correct.<br />
state.id = state.number<br />
state.id_length = 4<br />
end<br />
else<br />
error(string.format("unhandled message number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local block = run_parser(block_parser)<br />
table.insert(state,block)<br />
-- add min length of block to minimal length of message<br />
local min_length = block.min_length<br />
if block.has_count then<br />
-- add one byte for the block count<br />
min_length = min_length + 1<br />
state.fixed_length = false<br />
else<br />
-- if block is not fixed length then message can't be fixed length.<br />
if not block.fixed_length then<br />
state.fixed_length = false<br />
end<br />
min_length = min_length * block.count<br />
end<br />
state.min_length = state.min_length + min_length<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of message: " .. state.name)<br />
end,<br />
}<br />
<br />
--<br />
-- Template file parser<br />
--<br />
local template_parser = {<br />
name = "message_template",<br />
unhandled_error = false,<br />
init = function()<br />
return {<br />
version = 0,<br />
msg_count = 0,<br />
msgs = {}<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
-- handle version<br />
if cur_token_str == "version" then<br />
state.expect = Token.NUMBER<br />
state.last_ident = cur_token_str<br />
else<br />
error(string.format("unknown template identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
-- handle version number<br />
if state.last_ident == "version" then<br />
state.last_ident = nil<br />
state.version = tonumber(cur_token_str)<br />
-- check version number<br />
if state.version ~= 2 then<br />
error("invalid verion: " .. state.version)<br />
end<br />
else<br />
error(string.format("unhandled template number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local message = run_parser(message_parser)<br />
state.msg_count = state.msg_count + 1<br />
state.msgs[message.id] = message<br />
end,<br />
["}"] = function(state)<br />
error(string.format("unhandled '%s' token",cur_token_str))<br />
end,<br />
eof = function(state)<br />
return state<br />
end,<br />
}<br />
<br />
function parse_template(file)<br />
-- create lexer<br />
local status, ret = pcall(get_lexer,file)<br />
if not status then<br />
ret = string.format("LLUDP: Failed parse file into tokens: %s\n%s\n", file, ret)<br />
error(ret, 0)<br />
return nil<br />
end<br />
lexer = ret<br />
-- parse template file<br />
local status, ret = pcall(run_parser,template_parser)<br />
if not status then<br />
ret = string.format("LLUDP: Failed parsing on line %s:%d: '%s'\n%s\n",<br />
file, lexer.get_line_number(), lexer.get_line(), ret)<br />
error(ret, 0)<br />
return nil<br />
end<br />
io.write("finished parsing: " .. file .. "\n")<br />
-- return list of messages parsed from file.<br />
return ret<br />
end<br />
<br />
--parse_template("message_template.msg")<br />
--print_tokens("message_template.msg")<br />
<br />
</source><br />
<br />
=== File "lexer.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
-- Token types.<br />
Token = {<br />
NONE = -1,<br />
IDENTIFIER = -2,<br />
NUMBER = -3,<br />
COMMENT = -6,<br />
EOL = -7,<br />
["{"] = "{",<br />
["}"] = "}",<br />
}<br />
TokenNames = {<br />
[-1] = "NONE",<br />
[-2] = "IDENTIFIER",<br />
[-3] = "NUMBER",<br />
[-6] = "COMMENT",<br />
[-7] = "EOL",<br />
["{"] = "{",<br />
["}"] = "}",<br />
}<br />
<br />
-- parse line into array of tokens.<br />
local function default_parse_tokens(line)<br />
local tokens = {}<br />
local comment = nil<br />
-- check for a comment on this line.<br />
local idx = line:find("//")<br />
if idx ~= nil then<br />
comment = {Token.COMMENT, line:sub(idx)}<br />
-- remove comment from line<br />
line = line:sub(1,idx - 1)<br />
end<br />
<br />
-- split line into tokens using white-space as token delimitator<br />
for tok in line:gmatch("%s?([^%s]+)") do<br />
local tok_type = Token.NONE<br />
-- check for number<br />
if tonumber(tok) ~= nil then<br />
tok_type = Token.NUMBER<br />
elseif Token[tok] then<br />
-- token is same as type<br />
tok_type = Token[tok]<br />
else<br />
-- token is an identifier<br />
tok_type = Token.IDENTIFIER<br />
end<br />
table.insert(tokens,{tok_type,tok})<br />
end<br />
-- insert comment token.<br />
if comment ~= nil then<br />
table.insert(tokens,comment)<br />
end<br />
-- add token to mark the end of this line<br />
table.insert(tokens,{Token.EOL, ""})<br />
return tokens<br />
end<br />
<br />
function get_lexer(file, parse_tokens)<br />
-- use the default line tokenizer if one is not provided<br />
if parse_tokens == nil then parse_tokens = default_parse_tokens end<br />
-- next/current line code<br />
local line_num = 0<br />
local line = nil<br />
local next_line = io.lines(file)<br />
-- parse line tokens code<br />
local get_next_token = nil<br />
local next_tokens = function ()<br />
local f, tokens, idx<br />
repeat<br />
line_num = line_num + 1<br />
line = next_line()<br />
if line == nil then return nil end<br />
tokens = parse_tokens(line)<br />
until tokens ~= nil<br />
-- create get_next_toekn function from table iterator<br />
f, tokens, idx = ipairs(tokens)<br />
get_next_token = function()<br />
idx, token = f(tokens, idx)<br />
return token<br />
end<br />
return tokens<br />
end<br />
<br />
-- get first group of tokens<br />
if next_tokens() == nil then<br />
-- error reading file or empty file<br />
return nil<br />
end<br />
-- build lexer table.<br />
local lexer = {<br />
get_token = function ()<br />
local token<br />
repeat<br />
token = get_next_token()<br />
if token == nil then<br />
-- get next group of tokens<br />
if next_tokens() == nil then<br />
-- end of file.<br />
return nil<br />
end<br />
end<br />
until token ~= nil<br />
return token<br />
end,<br />
get_line_number = function() return line_num end,<br />
get_line = function() return line end<br />
}<br />
return lexer<br />
end<br />
<br />
function print_tokens(file)<br />
local lexer = get_lexer(file)<br />
local num = -1<br />
while true do<br />
local tok = lexer.get_token()<br />
if tok == nil then<br />
break<br />
end<br />
if num ~= lexer.get_line_number() then<br />
num = lexer.get_line_number()<br />
io.write("\n")<br />
io.write(string.format("%d: ",num))<br />
end<br />
io.write(string.format("%s ",tok[2]))<br />
end<br />
io.write("\n")<br />
end<br />
<br />
</source></div>Aiaustinhttp://opensimulator.org/wiki/LLUDP_DissectorLLUDP Dissector2023-01-14T17:38:04Z<p>Aiaustin: /* on Windows */ Updated install instructions for the LLUDP Dissector for Windows</p>
<hr />
<div>__NOTOC__<br />
{{Quicklinks}}<br />
<br /><br />
<br />
== LLUDP protocol dissector ==<br />
On this page you will find the Lua code for a wireshark protocol dissector that can parse the message_template.msg file and use that information to decode all the message fields from the Linden UDP protocol. The code is also available on [https://github.com/Neopallium/lludp_dissector GitHub]<br />
<br />
== Installing ==<br />
* Requires wireshark with Lua 5.1.x support. [http://wiki.wireshark.org/Lua See this page for getting wireshark to support lua]<br />
<br />
=== on Linux ===<br />
* Copy all four source files into ~/.wireshark<br />
* edit ''/etc/wireshark/init.lua'' (or equivalent on your system) and change ''disable_lua'' to ''false'' (default is true)<br />
* If you need to run wireshark as the root user or using sudo then you will need to edit the scripts into one file by replacing the dofile("script.lua") calls with the contents of file between the quotes.<br />
* The other method is to add your user account to the correct group (on Gentoo it is group "wireshark") that will allow your non-root user to capture packets.<br />
<br />
=== on Windows ===<br />
* Copy the lludp folder containing the five .lua source files into the Wireshark AppData directory <br />
<br />
'''Windows versions up to Windows 11'''<br />
<br />
C:\Users\<username>\AppData\Roaming\Wireshark\Plugins <br />
<br />
'''XP/2000'''<br />
<br />
C:\users\<username>\AppData\Wireshark<br />
<br />
* init.lua is not required as LUA support is enabled by default in recent versions of Wireshark.<br />
<br />
== LLUDP preferences ==<br />
There are three preferences that can be changed from wiresharks "Preferences" dialog:<br />
* Message template file: Full path to the message_template.msg file used to decode message name & details from the packets. On windows XP/Vista use double backslash '\\' instead of single blackslash '\' to separate directories (Example "C:\\Program Files\\SecondLife\\app_settings\\message_template.msg").<br />
* UDP port range start: First UDP port to mark as LLUDP packets. (default 13000)<br />
* UDP port range end: Last UDP port to mark as LLUDP packets. (default 13050)<br />
<br />
If your OpenSimulator regions are using ports 9000-9050 range then change the UDP port range.<br />
<br />
== Description of source files ==<br />
* "init.lua" -- simple script that loads the "lludp.lua" script.<br />
* "lludp.lua" -- contains the code that decodes each packet header and decompresses zero-encoded packets. This file uses wireshark only functions for accessing packet bytes and building a tree of information from each packet.<br />
* "llmessage.lua" -- contains the message_template.msg file parser the decodes the tokens from the lexer into an tree of tables containing all details about each message/block/variable from the template file. This file only has pure lua code.<br />
* "lexer.lua" -- contains the template file lexer. This lexer knows how to tokenize the template file into the follow tokens: IDENTIFIER, NUMBER, COMMENT, EOL. The stream of tokens produced by this lexer is parsed by the "llmessage.lua" file. This file only has pure lua code.<br />
<br />
=== Code license ===<br />
This Wireshark dissector maybe used under the terms of the "Simplified BSD License" or the [http://www.gnu.org/licenses/gpl.txt GPL]. -- Robert G. Jakabosky <bobby@sharedrealm.com><br />
<pre><br />
Simplified BSD License:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.<br />
<br />
</pre><br />
<br />
=== File "init.lua" ===<br />
<source lang="lua"><br />
<br />
-- register http to handle tcp ports 8000-8010 and 9000<br />
do<br />
local tcp_port_table = DissectorTable.get("tcp.port")<br />
local http_dissector = tcp_port_table:get_dissector(80)<br />
for port = 8000,8010 do<br />
tcp_port_table:add(port,http_dissector)<br />
end<br />
tcp_port_table:add(9000,http_dissector)<br />
end<br />
<br />
-- Load lludp protocol dissector.<br />
dofile("lludp.lua")<br />
<br />
</source><br />
<br />
=== File "lludp.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
dofile("llmessage.lua")<br />
<br />
-- cache globals to local for speed.<br />
local str_format=string.format<br />
<br />
-- test if ByteArray only has printable ASCII character and ends with '\0'<br />
local allowed_special = {<br />
[0] = true, -- null<br />
[9] = true, -- tab<br />
[10] = true, -- new line<br />
[13] = true, -- carriage return<br />
}<br />
local function is_string(bytes)<br />
local c<br />
local max = bytes:len() - 1<br />
for i=0,max do<br />
c = bytes:get_index(i)<br />
if c >= 127 then -- not ascii character<br />
return false<br />
elseif c < 32 then -- control character range<br />
if not allowed_special[c] then<br />
-- control characters between NULL and Space<br />
return false<br />
elseif c == 0 and i < max then<br />
-- null byte only allowed at end.<br />
return false<br />
end<br />
end<br />
end<br />
return true<br />
end<br />
<br />
-- lludp protocol example<br />
-- declare our protocol<br />
local lludp_proto = Proto("lludp","LLUDP","LindenLabs UDP Protocol")<br />
<br />
-- setup preferences<br />
lludp_proto.prefs["template_file"] =<br />
Pref.string("Message template file", "message_template.msg", "Message template file")<br />
lludp_proto.prefs["udp_port_start"] =<br />
Pref.string("UDP port range start", "13000", "First UDP port to decode as this protocol")<br />
lludp_proto.prefs["udp_port_end"] =<br />
Pref.string("UDP port range end", "13050", "Last UDP port to decode as this protocol")<br />
-- current preferences settings.<br />
local current_settings = {<br />
template_file = "",<br />
udp_port_start = -1,<br />
udp_port_end = -1,<br />
}<br />
-- current list of parsed messages.<br />
local message_details = nil<br />
<br />
-- setup protocol fields.<br />
lludp_proto.fields = {}<br />
local fds = lludp_proto.fields<br />
fds.flags = ProtoField.uint8("lludp.flags", "Flags", base.HEX, nil, 0xFF)<br />
fds.flags_zero = ProtoField.uint8("lludp.flags.zero", "Zero", base.HEX, nil, 0x80)<br />
fds.flags_reliable = ProtoField.uint8("lludp.flags.rel", "Reliable", base.HEX, nil, 0x40)<br />
fds.flags_resent = ProtoField.uint8("lludp.flags.res", "Resent", base.HEX, nil, 0x20)<br />
fds.flags_ack = ProtoField.uint8("lludp.flags.ack", "Ack", base.HEX, nil, 0x10)<br />
fds.sequence = ProtoField.uint32("lludp.sequence", "Sequence", base.DEC)<br />
fds.extra_len = ProtoField.uint8("lludp.extra_len", "Extra length", base.DEC)<br />
fds.extra_bytes = ProtoField.bytes("lludp.extra_bytes", "Extra header", base.HEX)<br />
fds.msg_id = ProtoField.uint32("lludp.msg.id", "Message ID", base.HEX)<br />
fds.msg_name = ProtoField.bytes("lludp.msg.name", "Message name")<br />
fds.msg = ProtoField.bytes("lludp.msg", "Message body", base.HEX)<br />
fds.acks_count = ProtoField.uint8("lludp.acks_count", "Acks count", base.DEC)<br />
fds.acks = ProtoField.uint32("lludp.acks", "Acks", base.DEC)<br />
fds.block_count = ProtoField.uint8("lludp.block_count", "Block count", base.DEC)<br />
fds.block = ProtoField.bytes("lludp.block", "Block", base.HEX)<br />
fds.var_fixed = ProtoField.bytes("lludp.var.fixed", "Fixed blob", base.HEX)<br />
fds.var_variable = ProtoField.bytes("lludp.var.variable", "Variable blob", base.HEX)<br />
fds.var_string = ProtoField.stringz("lludp.var.string", "String")<br />
fds.var_u8 = ProtoField.uint8("lludp.var.u8", "U8", base.DEC)<br />
fds.var_u16 = ProtoField.uint16("lludp.var.u16", "U16", base.DEC)<br />
fds.var_u32 = ProtoField.uint32("lludp.var.u32", "U32", base.DEC)<br />
fds.var_u64 = ProtoField.uint64("lludp.var.u64", "U64", base.DEC)<br />
fds.var_s8 = ProtoField.int8("lludp.var.s8", "S8", base.DEC)<br />
fds.var_s16 = ProtoField.int16("lludp.var.s16", "S16", base.DEC)<br />
fds.var_s32 = ProtoField.int32("lludp.var.s32", "S32", base.DEC)<br />
fds.var_s64 = ProtoField.int64("lludp.var.s64", "S64", base.DEC)<br />
fds.var_f32 = ProtoField.float("lludp.var.f32", "F32", base.DEC)<br />
fds.var_f64 = ProtoField.double("lludp.var.f64", "F64", base.DEC)<br />
fds.var_llvector3 = ProtoField.bytes("lludp.var.llvector3", "LLVector3", base.HEX)<br />
fds.var_llvector3d = ProtoField.bytes("lludp.var.llvector3d", "LLVector3d", base.HEX)<br />
fds.var_llvector4 = ProtoField.bytes("lludp.var.llvector4", "LLVector4", base.HEX)<br />
fds.var_llquaternion = ProtoField.bytes("lludp.var.llquaternion", "LLQuaternion", base.HEX)<br />
fds.var_lluuid = ProtoField.bytes("lludp.var.lluuid", "LLUUID", base.HEX)<br />
fds.var_bool = ProtoField.uint8("lludp.var.bool", "BOOL", base.DEC)<br />
fds.var_ipaddr = ProtoField.ipv4("lludp.var.ipaddr", "IPADDR", base.DEC)<br />
fds.var_ipport = ProtoField.uint16("lludp.var.ipport", "IPPORT", base.DEC)<br />
-- variable type handlers.<br />
local variable_handlers = {<br />
Fixed = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_fixed, rang)<br />
if len <= 4 then<br />
ti:set_text(str_format("%s: 0x%08x",var.name, rang:uint()))<br />
else<br />
ti:set_text(str_format("%s: length=%d, Blob:%s", var.name, len, tostring(rang)))<br />
end<br />
end,<br />
Variable = function(block_tree, buffer, offset, len, var)<br />
local is_data = false<br />
-- try to guess if this field is text.<br />
if var.name:find("Data") then<br />
-- this is a data find.<br />
is_data = true<br />
end<br />
local str_rang = buffer(offset + var.count_length, len - var.count_length)<br />
local bytes = str_rang:bytes()<br />
if not is_data and is_string(bytes) then<br />
local str = str_rang:string()<br />
local ti = block_tree:add(fds.var_string, buffer(offset, len))<br />
ti:set_text(str_format("%s: %s", var.name, str))<br />
else<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_variable, rang)<br />
if len <= 4 then<br />
ti:set_text(str_format("%s: 0x%08x",var.name, rang:uint()))<br />
else<br />
ti:set_text(str_format("%s: length=%d, Blob:%s", var.name, len, tostring(rang)))<br />
end<br />
end<br />
end,<br />
U8 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u8, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U16 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u16, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u32, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u64, rang)<br />
ti:set_text(str_format("%s: 0x%s",var.name, tostring(rang)))<br />
end,<br />
S8 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s8, rang)<br />
local num = rang:le_uint()<br />
if num > 127 then num = num - 256 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S16 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s16, rang)<br />
local num = rang:le_uint()<br />
if num > 32768 then num = num - 65536 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s32, rang)<br />
local num = rang:le_uint()<br />
if num > 2147483648 then num = num - 4294967296 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s64, rang)<br />
ti:set_text(str_format("%s: 0x%s",var.name, tostring(rang)))<br />
end,<br />
F32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_f32, rang)<br />
ti:set_text(str_format("%s: %f", var.name, rang:le_float()))<br />
end,<br />
F64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_f64, rang)<br />
ti:set_text(str_format("%s: %f", var.name, rang:le_float()))<br />
end,<br />
LLVector3 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector3, rang)<br />
-- parse LLVector3<br />
local x,y,z<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f>", var.name, x, y, z))<br />
end,<br />
LLVector3d = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector3d, rang)<br />
-- parse LLVector3d<br />
local x,y,z<br />
x = buffer(offset + 0,8):le_float()<br />
y = buffer(offset + 8,8):le_float()<br />
z = buffer(offset + 16,8):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f>", var.name, x, y, z))<br />
end,<br />
LLVector4 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector4, rang)<br />
-- parse LLVector4<br />
local x,y,z,s<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
s = buffer(offset + 12,4):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f,%f>", var.name, x, y, z, s))<br />
end,<br />
LLQuaternion = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llquaternion, rang)<br />
-- parse LLQuaternion<br />
local x,y,z,w<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
-- calculate W<br />
w = 1 - (x * x) - (y * y) - (z * z)<br />
if w > 0 then<br />
w = math.sqrt(w)<br />
else<br />
w = 0<br />
end<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f,%f>", var.name, x, y, z, w))<br />
end,<br />
LLUUID = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_lluuid, rang)<br />
local str = tostring(rang)<br />
str = str:sub(1,8) .. '-' ..<br />
str:sub(9,12) .. '-' .. str:sub(13,16) .. '-' ..<br />
str:sub(17,20) .. '-' .. str:sub(21)<br />
ti:set_text(str_format("%s: %s", var.name, str))<br />
end,<br />
BOOL = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_bool, rang)<br />
local val = "false"<br />
if rang:le_uint() > 0 then<br />
val = "true"<br />
end<br />
ti:set_text(str_format("%s: %s", var.name, val))<br />
end,<br />
IPADDR = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add(fds.var_ipaddr, rang)<br />
ti:set_text(str_format("%s: %s", var.name, tostring(rang:ipv4())))<br />
end,<br />
IPPORT = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add(fds.var_ipport, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:uint()))<br />
end,<br />
}<br />
<br />
-- un-register lludp to handle udp port range<br />
local function unregister_udp_port_range(start_port, end_port)<br />
if not start_port or start_port <= 0 or not end_port or end_port <= 0 then<br />
return<br />
end<br />
udp_port_table = DissectorTable.get("udp.port")<br />
for port = start_port,end_port do<br />
udp_port_table:remove(port,lludp_proto)<br />
end<br />
end<br />
<br />
-- register lludp to handle udp port range<br />
local function register_udp_port_range(start_port, end_port)<br />
if not start_port or start_port <= 0 or not end_port or end_port <= 0 then<br />
return<br />
end<br />
udp_port_table = DissectorTable.get("udp.port")<br />
for port = start_port,end_port do<br />
udp_port_table:add(port,lludp_proto)<br />
end<br />
end<br />
<br />
-- handle preferences changes.<br />
function lludp_proto.init(arg1, arg2)<br />
local old_start, old_end<br />
local new_start, new_end<br />
-- check if preferences have changed.<br />
for pref_name,old_v in pairs(current_settings) do<br />
local new_v = lludp_proto.prefs[pref_name]<br />
if new_v ~= old_v then<br />
if pref_name == "template_file" then<br />
-- load & parse message_template.msg file.<br />
local file = new_v<br />
if file and file:len() > 0 then<br />
local new_details = parse_template(file)<br />
if new_details then<br />
message_details = new_details<br />
end<br />
end<br />
elseif pref_name == "udp_port_start" then<br />
old_start = old_v<br />
new_start = new_v<br />
elseif pref_name == "udp_port_end" then<br />
old_end = old_v<br />
new_end = new_v<br />
end<br />
-- save new value.<br />
current_settings[pref_name] = new_v<br />
end<br />
end<br />
-- un-register old port range<br />
if old_start and old_end then<br />
unregister_udp_port_range(tonumber(old_start), tonumber(old_end))<br />
end<br />
-- register new port range.<br />
if new_start and new_end then<br />
register_udp_port_range(tonumber(new_start), tonumber(new_end))<br />
end<br />
end<br />
<br />
-- parse flag bits.<br />
local FLAG_ZER = 4<br />
local FLAG_REL = 3<br />
local FLAG_RES = 2<br />
local FLAG_ACK = 1<br />
local flag_names = {"ACK", "RES", "REL", "ZER"}<br />
local bits_lookup = {<br />
{},<br />
{1},<br />
{2},<br />
{2,1},<br />
{3},<br />
{3,1},<br />
{3,2},<br />
{3,2,1},<br />
{4},<br />
{4,1},<br />
{4,2},<br />
{4,2,1},<br />
{4,3},<br />
{4,3,1},<br />
{4,3,2},<br />
{4,3,2,1},<br />
}<br />
local function parse_flags(flags)<br />
flags = (flags / 16) + 1<br />
local bit_list = bits_lookup[flags]<br />
local bits = {}<br />
local names = ""<br />
for _, bit in ipairs(bit_list) do<br />
bits[bit] = true<br />
if names:len() > 0 then<br />
names = names .. ", "<br />
end<br />
names = names .. flag_names[bit]<br />
end<br />
return bits, names<br />
end<br />
<br />
local function grow_buff(buff, size)<br />
local old_size = buff:len()<br />
if old_size > size then return end<br />
-- buffer needs to grow<br />
buff:set_size(size)<br />
-- fill new space with zeros<br />
for i = old_size,size-1 do<br />
buff:set_index(i, 0)<br />
end<br />
end<br />
<br />
local function zero_decode(zero_buf)<br />
local out_buf = ByteArray.new()<br />
local zero_off = 0<br />
local zero_len = zero_buf:len()<br />
local out_size = 0<br />
local out_off = 0<br />
local b<br />
-- pre-allocate<br />
grow_buff(out_buf, zero_len)<br />
out_size = zero_len<br />
-- zero expand<br />
repeat<br />
b = zero_buf:get_index(zero_off)<br />
if b == 0 then<br />
-- get zero count<br />
local count = zero_buf:get_index(zero_off + 1)<br />
if count == 0 then count = 255 end<br />
out_off = out_off + count<br />
-- fill zeros<br />
if out_off > out_size then<br />
out_size = out_off + 128<br />
grow_buff(out_buf, out_size)<br />
end<br />
zero_off = zero_off + 2<br />
else<br />
if out_off >= out_size then<br />
out_size = out_off + (zero_len - zero_off) + 4<br />
grow_buff(out_buf, out_size)<br />
end<br />
-- copy non-zero bytes.<br />
out_buf:set_index(out_off,b)<br />
zero_off = zero_off + 1<br />
out_off = out_off + 1<br />
end<br />
until zero_off == zero_len<br />
-- truncate to real size.<br />
out_buf:set_size(out_off)<br />
<br />
return out_buf:tvb("Decompressed Data")<br />
end<br />
<br />
local function parse_msg_id(buff)<br />
local b = buff:get_index(0)<br />
local msg_id = b<br />
local msg_id_len = 1<br />
if b == 255 then<br />
b = buff:get_index(1)<br />
msg_id = msg_id * 256 + b<br />
msg_id_len = 2<br />
if b == 255 then<br />
b = buff:get_index(2)<br />
msg_id = msg_id * 256 + b<br />
b = buff:get_index(3)<br />
msg_id = msg_id * 256 + b<br />
msg_id_len = 4<br />
end<br />
end<br />
return msg_id, msg_id_len<br />
end<br />
<br />
-- get message name.<br />
local function get_msg_name(msg_id)<br />
-- check that we have message details<br />
if message_details == nil then<br />
return str_format("0x%08x", msg_id)<br />
end<br />
-- find message name from id.<br />
local msg = message_details.msgs[msg_id]<br />
-- Invalid message id<br />
if msg == nil then<br />
return str_format("0x%08x", msg_id)<br />
end<br />
return msg.name<br />
end<br />
<br />
-- calculate length a block.<br />
local function get_block_length(msg_buffer, start_offset, block)<br />
-- check if bock is fixed length.<br />
if block.fixed_length then<br />
return block.min_length<br />
end<br />
-- parse block's variables to calculate total block length.<br />
local offset = start_offset<br />
local rang<br />
for _,var in ipairs(block) do<br />
local len = 0<br />
if var.has_count then<br />
-- variable with length bytes.<br />
len = var.count_length<br />
--print(var.name, offset, ", len:", len)<br />
rang = msg_buffer(offset, len)<br />
len = len + rang:le_uint()<br />
--print(var.name, var.count_length, ", total:", len)<br />
else<br />
-- fixed length variable<br />
len = var.length<br />
--print(var.name, ", total:", len)<br />
end<br />
offset = offset + len<br />
end<br />
return (offset - start_offset)<br />
end<br />
<br />
-- build block tree<br />
local function build_block_tree(msg_buffer, block_tree, start_offset, block)<br />
local offset = start_offset<br />
local rang<br />
-- parse block's variables<br />
for _,var in ipairs(block) do<br />
local len = 0<br />
if var.has_count then<br />
-- variable with length bytes.<br />
len = var.count_length<br />
rang = msg_buffer(offset, len)<br />
len = len + rang:le_uint()<br />
else<br />
-- fixed length variable<br />
len = var.length<br />
end<br />
-- get variable's type field.<br />
local handler = variable_handlers[var.type]<br />
-- parse variable.<br />
if handler then<br />
handler(block_tree, msg_buffer, offset, len, var)<br />
end<br />
offset = offset + len<br />
end<br />
return (offset - start_offset)<br />
end<br />
<br />
-- buid message tree<br />
local function build_msg_tree(msg_buffer, msg_tree, msg_id)<br />
local offset = 0<br />
local rang<br />
-- check that we have message details<br />
if message_details == nil then<br />
msg_tree:set_text(str_format("Message Id: 0x%08x", msg_id))<br />
return nil<br />
end<br />
-- find message name from id.<br />
local msg = message_details.msgs[msg_id]<br />
-- Invalid message id<br />
if msg == nil then<br />
msg = str_format("Invalid message id: 0x%08x", msg_id)<br />
msg_tree:add_expert_info(PI_MALFORMED, PI_ERROR, msg)<br />
msg_tree:set_text(msg)<br />
return nil<br />
end<br />
-- skip message id bytes.<br />
offset = msg.id_length<br />
-- set message name.<br />
msg_tree:set_text(msg.name .. ":")<br />
-- proccess message blocks<br />
for _,block in ipairs(msg) do<br />
local count = block.count<br />
if count == nil then<br />
-- parse count byte.<br />
rang = msg_buffer(offset,1)<br />
count = rang:uint()<br />
msg_tree:add(fds.block_count,rang)<br />
offset = offset + 1<br />
end<br />
-- print("block name: ", block.name, count)<br />
for n=1,count do<br />
local block_len = get_block_length(msg_buffer, offset, block)<br />
-- parse block<br />
rang = msg_buffer(offset, block_len)<br />
local block_tree = msg_tree:add(fds.block,rang)<br />
if count > 1 then<br />
block_tree:set_text(str_format("%s: %d of %d",block.name,n,count))<br />
else<br />
block_tree:set_text(block.name)<br />
end<br />
-- parse block variables.<br />
build_block_tree(msg_buffer, block_tree, offset, block)<br />
offset = offset + block_len<br />
end<br />
end<br />
return msg.name<br />
end<br />
<br />
-- packet dissector<br />
function lludp_proto.dissector(buffer,pinfo,tree)<br />
local rang,offset<br />
pinfo.cols.protocol = "LLUDP"<br />
local lludp_tree = tree:add(lludp_proto,buffer(),"Linden UDP Protocol")<br />
-- Flags byte.<br />
offset = 0<br />
rang = buffer(offset,1)<br />
local flags = rang:uint()<br />
local flags_bits, flags_list = parse_flags(flags)<br />
flags_tree = lludp_tree:add(fds.flags, rang)<br />
flags_tree:set_text("Flags: " .. str_format('0x%02X (%s)', flags, flags_list))<br />
flags_tree:add(fds.flags_zero, rang)<br />
flags_tree:add(fds.flags_reliable, rang)<br />
flags_tree:add(fds.flags_resent, rang)<br />
flags_tree:add(fds.flags_ack, rang)<br />
offset = offset + 1<br />
-- Sequence number 4 bytes.<br />
rang = buffer(offset,4)<br />
local sequence = rang:uint()<br />
lludp_tree:add(fds.sequence, rang)<br />
offset = offset + 4<br />
-- Extra header length.<br />
rang = buffer(offset,1)<br />
local extra_length = rang:uint()<br />
lludp_tree:add(fds.extra_len,rang)<br />
offset = offset + 1<br />
-- Extra header data.<br />
if extra_length > 0 then<br />
rang = buffer(offset, extra_length)<br />
lludp_tree:add(fds.extra_bytes, rang)<br />
offset = offset + extra_length<br />
end<br />
-- Appended Acks. count<br />
local acks_bytes = 0<br />
local acks_count = 0<br />
if flags_bits[FLAG_ACK] then<br />
rang = buffer(buffer:len() - 1, 1)<br />
acks_count = rang:uint()<br />
acks_bytes = (acks_count * 4) + 1<br />
end<br />
-- Zero Decode<br />
local msg_len = (buffer:len() - acks_bytes) - offset<br />
if flags_bits[FLAG_ZER] then<br />
msg_buffer=zero_decode(buffer(offset,msg_len):bytes())<br />
msg_len = msg_buffer:len()<br />
offset = 0<br />
else<br />
msg_buffer = buffer(offset, msg_len):tvb()<br />
offset = 0<br />
end<br />
-- Message ID<br />
local msg_id, msg_id_len = -1, 4<br />
if msg_id_len > msg_len then<br />
msg_id_len = msg_len<br />
end<br />
msg_id, msg_id_len = parse_msg_id(msg_buffer(offset, msg_id_len):bytes())<br />
rang = msg_buffer(offset, msg_id_len)<br />
local msg_id_tree = lludp_tree:add(fds.msg_id, rang)<br />
local msg_name = get_msg_name(msg_id)<br />
if msg_name == nil then<br />
msg_id_tree:set_text(str_format("Message name: 0x%08x", msg_id))<br />
else<br />
msg_id_tree:set_text(str_format("Message name: %s",msg_name))<br />
end<br />
-- Message body.<br />
rang = msg_buffer(offset, msg_len)<br />
local msg_tree = lludp_tree:add(fds.msg, rang)<br />
build_msg_tree(msg_buffer, msg_tree, msg_id)<br />
-- Appended Acks. list.<br />
if flags_bits[FLAG_ACK] then<br />
local acks_off = buffer:len()<br />
rang = buffer(acks_off - 1, 1)<br />
acks_off = acks_off - acks_bytes<br />
local acks_tree = lludp_tree:add(fds.acks_count, rang)<br />
for i = 1,acks_count do<br />
rang = buffer(acks_off,4)<br />
acks_tree:add(fds.acks, rang)<br />
acks_off = acks_off + 4<br />
end<br />
end<br />
-- Info column<br />
pinfo.cols.info = str_format('[%s] Seq=%u Type=%s', flags_list, sequence, msg_name)<br />
end<br />
<br />
-- register lludp to handle udp ports 9000-9003<br />
register_udp_port_range(9000,9003)<br />
</source><br />
<br />
=== File "llmessage.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
dofile("lexer.lua")<br />
<br />
local lexer<br />
local cur_token = nil<br />
local cur_token_str = nil<br />
<br />
local function get_token(skip_tokens)<br />
repeat<br />
token = lexer.get_token()<br />
if token ~= nil then<br />
cur_token = token[1]<br />
cur_token_str = token[2]<br />
end<br />
until token == nil or not skip_tokens[cur_token]<br />
return token<br />
end<br />
<br />
local function run_parser(parser)<br />
local state = parser.init()<br />
if parser.skip_tokens == nil then parser.skip_tokens = {} end<br />
while get_token(parser.skip_tokens) do<br />
-- check what the parser is expecting next.<br />
if state.expect then<br />
-- check expected type<br />
if state.expect ~= cur_token then<br />
error(string.format("state.expected token '%s' instead of '%s'",<br />
TokenNames[state.expect], TokenNames[cur_token]))<br />
end<br />
-- reset expect field<br />
state.expect = nil<br />
end<br />
if state.expect_str then<br />
-- check expected string<br />
if state.expect_str ~= cur_token_str then<br />
error(string.format("state.expected token '%s' instead of '%s'",<br />
state.expect_str, cur_token_str))<br />
end<br />
-- reset expect_str field<br />
state.expect_str = nil<br />
end<br />
-- get handler function for current token.<br />
local f = parser[cur_token]<br />
if f ~= nil then<br />
local ret = f(state)<br />
if ret then<br />
-- praser finished.<br />
return ret<br />
end<br />
elseif parser.unhandled_error then<br />
error(string.format("unhandled token '%s' when paring '%s'\n", cur_token_str, parser.name))<br />
end<br />
end<br />
return parser.eof(state)<br />
end<br />
<br />
-- Known variable types and there fixed length.<br />
-- length == -1, requires a number after the type that is the length of count field<br />
-- length == -2, requires a number after the type that is the fixed variable length.<br />
VariableTypes = {<br />
Null = 0,<br />
Fixed = -2,<br />
Variable = -1,<br />
U8 = 1,<br />
U16 = 2,<br />
U32 = 4,<br />
U64 = 8,<br />
S8 = 1,<br />
S16 = 2,<br />
S32 = 4,<br />
S64 = 8,<br />
F32 = 4,<br />
F64 = 8,<br />
LLVector3 = 12,<br />
LLVector3d = 24,<br />
LLVector4 = 16,<br />
LLQuaternion = 12,<br />
LLUUID = 16,<br />
BOOL = 1,<br />
IPADDR = 4,<br />
IPPORT = 2,<br />
}<br />
<br />
--<br />
-- Variable parser<br />
--<br />
local variable_parser = {<br />
name = "variable",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
return {<br />
name = "<MISSING VARIABLE NAME>",<br />
type = "Null",<br />
has_count = false,<br />
length = 0,<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
required = 2<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "type"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "type" then<br />
state.type = cur_token_str<br />
state.length = VariableTypes[state.type]<br />
if state.length == nil then<br />
error("Unknown variable type: " .. cur_token_str)<br />
elseif state.length == -1 or state.length == -2 then<br />
state.expect = Token.NUMBER<br />
else<br />
state.required = state.required - 1<br />
end<br />
else<br />
error(string.format("unhandled variable identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "type" then<br />
if state.length == -1 then<br />
-- variable field length uses embedded count field<br />
state.has_count = true<br />
state.count_length = tonumber(cur_token_str)<br />
state.length = nil<br />
elseif state.length == -2 then<br />
-- fixed field length<br />
state.length = tonumber(cur_token_str)<br />
end<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled variable number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
error("sub block not allowed in variable block")<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of variable: " .. state.name)<br />
end,<br />
}<br />
<br />
-- Block Quantities<br />
local BlockQuantity = {<br />
Single = 1,<br />
Variable = -1,<br />
Multiple = -2,<br />
}<br />
<br />
--<br />
-- Block parser<br />
--<br />
local block_parser = {<br />
name = "block",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
return {<br />
name = "<MISSING BLOCK NAME>",<br />
quantity = "Single",<br />
count = 0,<br />
min_length = 0,<br />
fixed_length = true,<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
required = 2<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "quantity"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "quantity" then<br />
state.quantity = cur_token_str<br />
state.count = BlockQuantity[cur_token_str]<br />
if state.count == nil then<br />
error("Unknown block quantity: " .. cur_token_str)<br />
elseif state.count == -2 then<br />
state.expect_field = "count"<br />
state.expect = Token.NUMBER<br />
else<br />
if state.count == -1 then<br />
state.has_count = true<br />
state.count_length = 1<br />
state.count = nil<br />
end<br />
state.required = state.required - 1<br />
end<br />
else<br />
error(string.format("unhandled block identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "count" then<br />
state.count = tonumber(cur_token_str)<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled block number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local variable = run_parser(variable_parser)<br />
table.insert(state,variable)<br />
-- add length of fixed length variables to minimal length of block.<br />
if variable.has_count then<br />
state.min_length = state.min_length + variable.count_length<br />
state.fixed_length = false<br />
else<br />
state.min_length = state.min_length + variable.length<br />
end<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of block: " .. state.name)<br />
end,<br />
}<br />
<br />
--<br />
-- Message parser<br />
--<br />
local message_parser = {<br />
name = "message",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
-- create state<br />
return {<br />
name = "<MISSING MESSAGE NAME>",<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
fixed_length = true,<br />
min_length = 0,<br />
required = 5<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "frequency"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "frequency" then<br />
state.frequency = cur_token_str<br />
state.expect = Token.NUMBER<br />
state.required = state.required - 1<br />
elseif state.expect_field == "trust" then<br />
state.trust = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "compression"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "compression" then<br />
state.compression = cur_token_str<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled message identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "frequency" then<br />
state.number = tonumber(cur_token_str)<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "trust"<br />
state.required = state.required - 1<br />
-- create true message id from frequency and message number<br />
local freq = state.frequency<br />
if freq == "High" then<br />
-- High is already correct.<br />
state.id = state.number<br />
state.id_length = 1<br />
elseif freq == "Medium" then<br />
state.id = tonumber("0xFF" .. string.format("%02X", state.number))<br />
state.id_length = 2<br />
elseif freq == "Low" then<br />
state.id = tonumber("0xFFFF" .. string.format("%04X", state.number))<br />
state.id_length = 4<br />
else<br />
-- Fixed is already correct.<br />
state.id = state.number<br />
state.id_length = 4<br />
end<br />
else<br />
error(string.format("unhandled message number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local block = run_parser(block_parser)<br />
table.insert(state,block)<br />
-- add min length of block to minimal length of message<br />
local min_length = block.min_length<br />
if block.has_count then<br />
-- add one byte for the block count<br />
min_length = min_length + 1<br />
state.fixed_length = false<br />
else<br />
-- if block is not fixed length then message can't be fixed length.<br />
if not block.fixed_length then<br />
state.fixed_length = false<br />
end<br />
min_length = min_length * block.count<br />
end<br />
state.min_length = state.min_length + min_length<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of message: " .. state.name)<br />
end,<br />
}<br />
<br />
--<br />
-- Template file parser<br />
--<br />
local template_parser = {<br />
name = "message_template",<br />
unhandled_error = false,<br />
init = function()<br />
return {<br />
version = 0,<br />
msg_count = 0,<br />
msgs = {}<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
-- handle version<br />
if cur_token_str == "version" then<br />
state.expect = Token.NUMBER<br />
state.last_ident = cur_token_str<br />
else<br />
error(string.format("unknown template identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
-- handle version number<br />
if state.last_ident == "version" then<br />
state.last_ident = nil<br />
state.version = tonumber(cur_token_str)<br />
-- check version number<br />
if state.version ~= 2 then<br />
error("invalid verion: " .. state.version)<br />
end<br />
else<br />
error(string.format("unhandled template number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local message = run_parser(message_parser)<br />
state.msg_count = state.msg_count + 1<br />
state.msgs[message.id] = message<br />
end,<br />
["}"] = function(state)<br />
error(string.format("unhandled '%s' token",cur_token_str))<br />
end,<br />
eof = function(state)<br />
return state<br />
end,<br />
}<br />
<br />
function parse_template(file)<br />
-- create lexer<br />
local status, ret = pcall(get_lexer,file)<br />
if not status then<br />
ret = string.format("LLUDP: Failed parse file into tokens: %s\n%s\n", file, ret)<br />
error(ret, 0)<br />
return nil<br />
end<br />
lexer = ret<br />
-- parse template file<br />
local status, ret = pcall(run_parser,template_parser)<br />
if not status then<br />
ret = string.format("LLUDP: Failed parsing on line %s:%d: '%s'\n%s\n",<br />
file, lexer.get_line_number(), lexer.get_line(), ret)<br />
error(ret, 0)<br />
return nil<br />
end<br />
io.write("finished parsing: " .. file .. "\n")<br />
-- return list of messages parsed from file.<br />
return ret<br />
end<br />
<br />
--parse_template("message_template.msg")<br />
--print_tokens("message_template.msg")<br />
<br />
</source><br />
<br />
=== File "lexer.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
-- Token types.<br />
Token = {<br />
NONE = -1,<br />
IDENTIFIER = -2,<br />
NUMBER = -3,<br />
COMMENT = -6,<br />
EOL = -7,<br />
["{"] = "{",<br />
["}"] = "}",<br />
}<br />
TokenNames = {<br />
[-1] = "NONE",<br />
[-2] = "IDENTIFIER",<br />
[-3] = "NUMBER",<br />
[-6] = "COMMENT",<br />
[-7] = "EOL",<br />
["{"] = "{",<br />
["}"] = "}",<br />
}<br />
<br />
-- parse line into array of tokens.<br />
local function default_parse_tokens(line)<br />
local tokens = {}<br />
local comment = nil<br />
-- check for a comment on this line.<br />
local idx = line:find("//")<br />
if idx ~= nil then<br />
comment = {Token.COMMENT, line:sub(idx)}<br />
-- remove comment from line<br />
line = line:sub(1,idx - 1)<br />
end<br />
<br />
-- split line into tokens using white-space as token delimitator<br />
for tok in line:gmatch("%s?([^%s]+)") do<br />
local tok_type = Token.NONE<br />
-- check for number<br />
if tonumber(tok) ~= nil then<br />
tok_type = Token.NUMBER<br />
elseif Token[tok] then<br />
-- token is same as type<br />
tok_type = Token[tok]<br />
else<br />
-- token is an identifier<br />
tok_type = Token.IDENTIFIER<br />
end<br />
table.insert(tokens,{tok_type,tok})<br />
end<br />
-- insert comment token.<br />
if comment ~= nil then<br />
table.insert(tokens,comment)<br />
end<br />
-- add token to mark the end of this line<br />
table.insert(tokens,{Token.EOL, ""})<br />
return tokens<br />
end<br />
<br />
function get_lexer(file, parse_tokens)<br />
-- use the default line tokenizer if one is not provided<br />
if parse_tokens == nil then parse_tokens = default_parse_tokens end<br />
-- next/current line code<br />
local line_num = 0<br />
local line = nil<br />
local next_line = io.lines(file)<br />
-- parse line tokens code<br />
local get_next_token = nil<br />
local next_tokens = function ()<br />
local f, tokens, idx<br />
repeat<br />
line_num = line_num + 1<br />
line = next_line()<br />
if line == nil then return nil end<br />
tokens = parse_tokens(line)<br />
until tokens ~= nil<br />
-- create get_next_toekn function from table iterator<br />
f, tokens, idx = ipairs(tokens)<br />
get_next_token = function()<br />
idx, token = f(tokens, idx)<br />
return token<br />
end<br />
return tokens<br />
end<br />
<br />
-- get first group of tokens<br />
if next_tokens() == nil then<br />
-- error reading file or empty file<br />
return nil<br />
end<br />
-- build lexer table.<br />
local lexer = {<br />
get_token = function ()<br />
local token<br />
repeat<br />
token = get_next_token()<br />
if token == nil then<br />
-- get next group of tokens<br />
if next_tokens() == nil then<br />
-- end of file.<br />
return nil<br />
end<br />
end<br />
until token ~= nil<br />
return token<br />
end,<br />
get_line_number = function() return line_num end,<br />
get_line = function() return line end<br />
}<br />
return lexer<br />
end<br />
<br />
function print_tokens(file)<br />
local lexer = get_lexer(file)<br />
local num = -1<br />
while true do<br />
local tok = lexer.get_token()<br />
if tok == nil then<br />
break<br />
end<br />
if num ~= lexer.get_line_number() then<br />
num = lexer.get_line_number()<br />
io.write("\n")<br />
io.write(string.format("%d: ",num))<br />
end<br />
io.write(string.format("%s ",tok[2]))<br />
end<br />
io.write("\n")<br />
end<br />
<br />
</source></div>Aiaustinhttp://opensimulator.org/wiki/ConfigurationConfiguration2023-01-14T13:26:54Z<p>Aiaustin: /* Running OpenSimulator in Standalone mode */ OpenSim.ini.example PublicPort changed to 9000 in 0.9.2.2 dev master code from 14-Jan-2923.</p>
<hr />
<div>{{Quicklinks}}<br />
<br /><br />
== OpenSimulator simulator configuration file ==<br />
The region simulator configuration is managed using a file called OpenSim.ini. This file is used regardless of whether the sim is running in standalone or grid mode. This file references some additional configuration information from the config-include/ directory. Information about the various settings is contained in the [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] file itself (or OpenSim.ini.example for reference).<br />
<br />
Please note, that the name [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] can be changed via [[OpenSim.exe Command Line Options|command line arguments]].<br />
<br />
It is also possible to distribute the inifile settings over two files. This is useful if you want to run several OpenSimulator processes where most of your settings are identical except for a few. The master file is read first, then the inifile is read. Settings given in the inifile overrule settings given in the master file. The master file has the same format and the same keywords as the inifile, so the same documentation applies.<br />
<br />
== Database ==<br />
Opensimulator supports the following database engines. Information about setting these up can be found in the OpenSim.ini.example file and the other various example files in bin/config-include. '''If you do not want to use the default SQLite configuration then you will need to setup your database before proceeding further'''. SQLite does not require further configuration. See [[Database Settings]] for the detailed settings.<br />
<br />
* '''SQLite''' (default) - a lightweight database that comes bundled with OpenSimulator and can be used without requiring any extra configuration. It is mostly intended to get you up and running quickly, not for production use. It is significantly slower than MySQL. A few features here (such as attachment persistence) have not yet been fully implemented. <br />
<br />
* '''MySQL''' (fully supported) - This is the recommended database for any use beyond experimentation or small standalone applications. The minimum MySQL version is 5.1.<br />
* '''MariaDB''' (fully supported) - A compatible alternative to MySQL. You need to make sure the selected charset is utf8mb3 (ie 3 bytes). Some instalations default to uft8mb4 (4 bytes) and that will fail<br />
<br />
* '''MSSQL''' (fairly supported) - persistence support for some recent OpenSimulator features may not yet be implemented though the vast majority of them are supported.<br />
<br />
== Standalone vs. Grid ==<br />
We recommend that you first get OpenSimulator running in standalone mode before you attempt to connect it to a grid or run your own grid. OpenSimulator will start up in standalone mode out-of-the-box on the binary distributions.<br />
<br />
An OpenSimulator configuration consists of regions (run by region simulators) and backend data services (such as user, assets and inventory management).<br />
<br />
A system running in '''standalone mode''' runs both the region simulator and all the data services in a single process when you run OpenSim.exe. In this mode you can run as many regions as you like but only on a single machine.<br />
<br />
[[Image:Opensim-standalone.png|frame|center|OpenSimulator running in standalone mode. Both simulator and services run in the same process (OpenSim.exe).]]<br />
<br />
In '''grid mode''', the data services are not part of the region server process. Instead, they are run in a separate executable called Robust.exe. A Robust shell can run all the services or they can be split amongst any number of Robust instances. This allows them to be run on entirely separate machines if necessary. In this mode, the OpenSim.exe acts solely as the region server, serving one or more regions that communicate with the separate data services. At this point you can run multiple OpenSim.exe region simulators on different machines.<br />
<br />
[[Image:Opensim-grid-simple.png|frame|center|OpenSimulator running in grid mode. In this case, all the services are being run within a Robust.exe process. Multiple copies of OpenSim.exe (usually running on different machines) all use the same set of common services.]]<br />
<br />
Running in grid mode is more complicated than running in standalone mode. It requires an understanding of UUID, X,Y location, server handshake passwords, estates and estate owners, and a couple of other settings. These require more care and patience to set up. We strongly recommend that you don't attempt this unless you are extremely patient and very technically proficient.<br />
<br />
= Running OpenSimulator in Standalone mode =<br />
<br />
Binary distributions of OpenSimulator are by default configured to run in standalone mode. For versions up to 0.9.2.1 you need also to edit bin/OpenSim.ini to change PublicPort = 8002 to PublicPort = 9000.<br />
<br />
However, if you build OpenSimulator from the source distribution or from the git repository then you will need to:<br />
<br />
# Change into the '''''bin''''' folder<br />
# Copy the file '''''OpenSim.ini.example''''' to '''''[[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]]'''''. This configures the 3D simulator itself.<br />
# Check the '''[Const]''' section PublicPort. It should be set to 9000 by default for a standalone.<br />
# In the '''[Architecture]''' section of '''''[[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]]''''', near the bottom of the file, uncomment the ''Standalone.ini'' line. To uncomment a line of code, remove the semi-colon (;) comment symbol preceding the line.<br />
# Change into the '''''bin/config-include''''' folder<br />
# Copy the file '''''StandaloneCommon.ini.example''''' to '''''StandaloneCommon.ini'''''. This configures the in-process data services used by the standalone configuration.<br />
# Copy the file '''''FlotsamCache.ini.example''''' to '''''FlotsamCache.ini'''''. This configures the cache services used by the standalone configuration.<br />
<br />
Running OpenSimulator is then a matter of launching OpenSim.exe. However, you need to have installed all dependencies before that. See [[Dependencies]] for details. After that, open a command prompt (for Windows users, Start menu > Run > cmd) and navigate to the Opensim /bin directory.<br />
<br />
Under '''Windows''', run:<br />
OpenSim.exe<br />
On '''Linux''' run:<br />
mono OpenSim.exe<br />
<br />
== Running for the first time ==<br />
<br />
If you're running OpenSimulator for the first time, it will ask you several questions at the console that will set up a single region for you. The configuration options you enter will be written to the bin/Regions/Regions.ini file, which you can then edit at a later date if you need to make changes.<br />
<br />
Many of the questions have defaults. Here are some explanations of the questions asked:<br />
<br />
* '''New region name'''<br />
::The name for your region. Don't leave this blank!<br />
* '''Region UUID'''<br />
::The unique ID of your region. In pretty much all cases you will want to accept the randomly generated default in the square brackets. The only time when you wouldn't is if you were trying to set up a configuration to point to pre-existing region data. But in this case you are probably better off editing the Regions.ini file directly anyway<br />
* '''Region Location'''<br />
::This is the location of the region on the grid. In standalone mode you can safely leave these as the default (1000,1000). If you were to set up additional regions later on in Regions.ini then they would need different grid co-ordinates (e.g. 1000,1001). OpenSimulator regions can be placed anywhere on a 65536 by 65536 grid, but [[Hypergrid]] enabled regions may need special consideration for region location. See [[Installing and Running Hypergrid#The 4096 Regions Limit]] for more information.<br />
* '''Internal IP address'''<br />
::In virtually all cases this can be left as 0.0.0.0 (this is a wildcard that allows OpenSimulator to listen for UDP connections on any of the server's network interfaces). If you want to restrict UDP connections to only one network interface then you can specify an explicit IP address. This address is only used internally - the '''External host name''' is the one that is actually passed to the viewer (and hence is the important one).<br />
* '''Internal port'''<br />
::This is the IP port for all incoming client connections. The name is a bit misleading since it will be used externally (by a Second Life viewer, for instance) as well as internally. You can make this any port you want, but it is safe to leave at the default 9000. Each region on your server must have a unique port.<br />
* '''Allow alternate ports'''<br />
::This is currently experimental. Please leave it at the default of False.<br />
* '''External host name'''<br />
::If you leave this at the default 'SYSTEMIP' then this will become the LAN network address of the machine (e.g. 192.168.1.2). This is fine if you are connecting only from within your LAN. If you want to connect to it from a client on the internet, this should be the External IP Address of your router. Fully Qualified Domain Names (FQDNs) can also be used though they will be converted to a numeric IP address before being sent to the viewer.<br />
<br />
OpenSimulator will ask you to assign each region to an estate during the setup process. If an estate needs to be created then it will also ask you to assign an estate manager. In standalone mode, an estate manager can also be created during the setup process.<br />
<br />
Don't forget the account details you use to set up the master avatar (in 0.6.9) or the estate manager (in 0.7 and later). Only this user will initially be able to configure the in-world settings for your region. This is also a user account that you can use to perform your initial login test.<br />
<br />
See [[Configuring Regions]] for more information about the Regions.ini file that these questions generate.<br />
<br />
If you want to create a user other than the estate manager, then in the server console type:<br />
<br />
create user<br />
<br />
This will ask you a series of questions for creating a user (such as first name, last name and password).<br />
<br />
== Network access for the standalone installation ==<br />
In standalone mode, a viewer connecting to your installation needs the following network access to your installation machine.<br />
<br />
1. TCP over the http_listener_port as used in your simulator. This is set in the [Network] section of your [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]].<br />
<br />
<source lang="ini"><br />
[Network]<br />
http_listener_port = 9000<br />
</source><br />
<br />
This will be the default of 9000 if you have not explicitly changed it.<br />
<br />
Set PublicPort to same port in section [Const]<br />
<br />
<source lang="ini"><br />
[Const]<br />
PublicPort = 9000<br />
</source><br />
<br />
2. UDP over each region's InternalPort as configured in your region files (such as Regions.ini). For instance, if you have configured<br />
<br />
<source lang="ini"><br />
[test]<br />
RegionUUID = dd5b77f8-bf88-45ac-aace-35bd76426c81<br />
Location = 1000,1000<br />
SizeX = 256<br />
SizeY = 256<br />
SizeZ = 256<br />
InternalAddress = 0.0.0.0<br />
InternalPort = 9000<br />
AllowAlternatePorts = False<br />
ExternalHostName = mygrid.com<br />
<br />
[test2]<br />
RegionUUID = dd5b77f8-bf88-45ac-aace-35bd76426c82<br />
Location = 1000,1001<br />
SizeX = 256<br />
SizeY = 256<br />
SizeZ = 256<br />
InternalAddress = 0.0.0.0<br />
InternalPort = 9001<br />
AllowAlternatePorts = False<br />
ExternalHostName = mygrid.com<br />
</source><br />
<br />
then you will need to open ports 9000 and 9001 to UDP traffic.<br />
<br />
3. The network address of the machine hosting the OpenSimulator installation must be accessible to connecting viewers. In the example above, the installation machine is reachable from the Internet via the domain name "mygrid.com". If the same installation needs to be accessed by viewers on the same network, it must be possible for them to also successfully resolve that domain name (not all routers, especially home routers, have this "loopback capability").<br />
<br />
If the installation only needed to be accessed on the same LAN, then one could you the local IP address of the server (e.g. 192.168.1.2).<br />
<br />
== Connecting to a standalone installation ==<br />
<br />
To connect to your new sim with your user, start up a viewer with the following command line switches:<br />
<br />
'''Client on same machine as OpenSim:'''<br />
-loginuri http://127.0.0.1:9000<br />
<br />
'''Client on same LAN as OpenSim:'''<br />
-loginuri http://lan_ip:9000<br />
<br />
'''Client on different machine or internet:'''<br />
-loginuri http://external_ip:9000<br />
<br />
Then enter the user name and password you set up in the previous step and your new user should login.<br />
<br />
Be aware of [http://osgrid.org/forums/viewtopic.php?f=5&t=400&start=0&st=0&sk=t&sd=a loopback] problems when Running viewer &amp; server(s) on the same machine (LAN) by using the "external" configuration. (<u>'''You might notice endless waiting for region handshake'''</u>.) See also [[Troubleshooting|troubleshoot hints]]. If you're having Connectivity problems, [[Network Settings|be sure to read the Network Configuration Page]]. This is important if you see Region handshake issue.<br />
<br />
[[Image:Exclamation.png|left]]<br />
<br />
== IMPORTANT NOTE, DIVA DISTRO - 4 Apr. 2012 - ==<br />
<br />
'''If you download the latest version of diva-r18611.tar.bz''', it is necessary to first launch the setup program ''configure.exe''<br />
*In Linux or MacOSX : open a terminal and enter "mono /diva-r18611/bin/Configure.exe" (assuming that you have placed the Diva distro in /diva-r18611)<br />
*In Windows, assuming they extracted Diva in My Documents, one would open "Run => cmd" and enter '''cd "%USERPROFILE%\My Documents\diva-r18611\"'', followed by "Configure.exe". <br />
After issuing the command, you can set your sim's domain name, and carefully answer the program's questions, then start the program as instructed in above paragraphs.<br />
<br />
The program will install the optimum configuration for OpenSim, example: '''<nowiki>http://<your_IP>:9000</nowiki>''' and WiFi '''<nowiki>http:<your_IP>:9000/wifi</nowiki>''' <br />
In the standalone version, four regions will be set up. You can optionally add other regions later on, so make sure to use the same first name with the addition of a number <br />
(ex: "region 5", "region 6", "region 7", etc. otherwise you can't enter the region and you'd be placed in the nearest free location.<br />
<br />
If you wish to enter a different region name, make sure that the "distance" between the island created by the Wifi configuration program and the next, <br />
will be at least 40 positions away from the first installed region)<br />
(command console: create region Johnnyland RegionConfigure.ini)<br />
<br />
= Running OpenSimulator in Grid mode =<br />
<br />
Running OpenSimulator in grid mode is considerably more complicated than running a standalone instance. Instead of running everything in the same process, backend data services (asset, inventory, etc.) run in one or more separate processes, often on a different machine. This allows multiple OpenSim.exe simulator instances to use the same asset and inventory data.<br />
<br />
== Step 1: Set up a ROBUST services instance ==<br />
<br />
1. In the bin directory, copy Robust.ini.example to Robust.ini. The example file is configured to run all the services in a single ROBUST instance.<br />
<br />
2. Configure the [[Database Settings]] in Robust.ini to use your MySQL database. Only MySQL is supported for running grid services. <br />
<br />
3. Start up Robust.exe. <br />
<br />
mono Robust.exe (Linux, BSD, Mac OS X)<br />
<br />
or<br />
<br />
Robust.exe (Windows)<br />
<br />
If you don't see any errors (in red) on the console then you can move on to the next step.<br />
<br />
4. Every region must belong to an estate, and every estate must have an owner which is a valid user account in OpenSim's user account service. Create a user on the ROBUST command console with the following command.<br />
<br />
create user<br />
<br />
This will ask you for the user's name, password and an optional e-mail. Remember this name since you will need it when you start up the simulator for the first time.<br />
<br />
== Step 2: Configure an OpenSim.exe to use the ROBUST services ==<br />
<br />
In grid mode, as in standalone mode, you need to configure [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] which controls the 3D simulator itself.<br />
<br />
However, instead of using and configuring the file config-include/StandaloneCommon.ini, a simulator connecting to a grid needs to use and configure the config-include/GridCommon.ini file, in order to connect to the ROBUST hosted remote data services rather than in-process local ones.<br />
<br />
The steps for both these operations are as follows.<br />
<br />
1. Copy bin/OpenSim.ini.example to [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]]<br />
<br />
2. Find the [Const] section near top of the file and set the parameters match the grid services ports<br />
<br />
<source lang="ini"><br />
[Const]<br />
BaseHostname = "MyNiceGrid.org"<br />
PublicPort = "8002"<br />
PrivatePort = "8003"<br />
</source><br />
<br />
3. Find the [Architecture] section at the very bottom of OpenSim.ini. Make sure that one of the following lines is uncommented:<br />
<br />
Include-Architecture = "config-include/Grid.ini" (in OpenSimulator 0.7.1 and later)<br />
<br />
or<br />
<br />
Include-Grid = "config-include/Grid.ini" (in OpenSimulator 0.7.0.2 and earlier)<br />
<br />
The others should remain commented.<br />
<br />
3. Go to bin/config-include and copy GridCommon.ini.example to GridCommon.ini.<br />
<br />
4. Open GridCommon.ini in a text editor. You will see lots of URL entries, each of which have dummy defaults of http://myassetserver.com:8003, http://myinventoryserver.com:8003, etc. You will need to change each of these to point towards the address of your ROBUST instance. For instance, if you're running ROBUST on a machine with a local IP address of 192.168.1.2, you will need to change AssetServerURI to the setting<br />
<br />
AssetServerURI = "http://192.168.1.2:8003"<br />
<br />
5. Run OpenSim.exe. If you're running OpenSim.exe for the first time you will get the same questions about setting up the region that occur on a first-run in standalone mode. Please see the standalone section for instructions on how to answer these, or read more information about the Regions.ini file on the [[Configuring Regions]] page.<br />
<br />
If everything is set up correctly, when starting up OpenSim.exe you shouldn't see any errors. You should also see the ROBUST console display log lines saying that the region has registered with the grid service. For example,<br />
<br />
21:43:45 - [GRID SERVICE]: Region t1 (176cc95e-f693-4b02-8e08-af86e2372faa) registered successfully at 256000-256000<br />
21:43:47 - [GRID SERVICE]: region t1 has 0 neighbours<br />
<br />
== Network access for a grid installation ==<br />
In standalone mode, a viewer connecting to your installation needs to access<br />
<br />
# The login service over TCP and other configured public services (e.g. grid info, map).<br />
# The http_server_port of each configured simulator over TCP.<br />
# The InternalPort and ExternalHostName of each configured region.<br />
<br />
The last two requirements are the same as for standalone installations, except that they apply to each server that hosts a simulator. Please see the standalone section above for more details.<br />
<br />
The login service is a little different. Whereas in standalone this uses the same http_server_port as the simulator itself, in grid mode it's running in a separate ROBUST service.<br />
<br />
The default port for the login service is 8002. You can see this used in the [ServiceList] section of Robust.ini.example.<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector"<br />
InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector"<br />
GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector"<br />
GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector"<br />
AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector"<br />
OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector"<br />
AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector"<br />
LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector"<br />
PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector"<br />
UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector"<br />
GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector"<br />
FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector"<br />
MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector"<br />
MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"<br />
</source><br />
<br />
Here all the public services (those where the viewer connects directly to the service) are served on port 8002, with internal services on 8003. So you need to make sure that the viewer can access port 8002 over TCP but you do not want to expose port 8003. Only simulators need to be able to connect to port 8003.<br />
<br />
== Connecting to a grid installation ==<br />
<br />
Your client startup line will look something like<br />
<br />
-loginuri http://mygrid.com:8002<br />
<br />
The loginuri needs to be the address to the login service. In standalone mode, this was the same address as the region simulator and the port was 9000 by default. However, in grid mode it will be the address to login service hosted on the ROBUST instance. In this case, the address will be 192.168.1.2. The port number of 8002 is the traditional one for the grid login service and is the default in Robust.ini.example.<br />
<br />
If the login is successful, you will see log lines on the ROBUST console (for the login itself) and then log lines on the region simulator console (as the login process tells the simulator to expect the avatar, tells the viewer the address of the region simulator and then when the viewer starts talking to the simulator directly).<br />
<br />
= Running multiple ROBUST service instances =<br />
<b>WARNING:<br><br />
This is a tricky issue and contrary to past allegations, opensim is not well prepared for this kind of microservices concepts.<br><br />
You can split some services by different machines, if they have no interdependencies. For example current XBakes can run anywhere<br><br />
Running several instances of same service is a invite to fail.<br><br />
Some services do assume they are the only instance, like current PresenceService, GriduserService, etc, and<br />
even do own caching to reduce load on the database engine, and that cache cannot be syncronized across instances<br><br />
Most will only service http in paralell, placing the same, and most of the load, on the same database engine. So unless that database service is a distributed system, doing proper local caching and synchronization with a master database, the gains of having multiple instances is not that large, if any if if not a total fail<br><br />
<br />
Another important thing to note is that currently several services need to read data from the configuration of other services. For example Gridservice needs to read data from its section, GridService, and sections LoginService, Hypergrid and GridInfoService. So the ini file for the robust instance running it needs to have those sections, and of course updated. <br />
</b><br><br />
<br />
<br />
If you are operating a grid, then you can run different services (e.g. asset, inventory) in different ROBUST instances, in order to spread the load. To do this, you will need to edit the ServiceConnectors parameter in the [Startup] section of Robust.ini (or [[Configuration/files/Robust/Robust.HG.ini|Robust.HG.ini]] if you're running Hypergrid). The default ServiceConnectors parameter looks something like this<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector"<br />
InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector"<br />
GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector"<br />
GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector"<br />
AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector"<br />
OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector"<br />
AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector"<br />
LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector"<br />
PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector"<br />
UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector"<br />
GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector"<br />
FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector"<br />
MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector"<br />
MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"<br />
</source><br />
<br />
Each entry has the form <br />
<br />
<port-number>/<dll>:<connector-class-name><br />
<br />
For instance, the first entry above<br />
<br />
8003/OpenSim.Server.Handlers.dll:AssetServiceConnector<br />
<br />
says to start an AssetServiceConnector (and hence an asset service) from the OpenSim.Server.Handlers.dll and to server that from port 8003.<br />
<br />
By default, Robust.exe loads a configuration file with the same name but with .ini appended instead of .exe. So Robust.exe will look for an inifile called Robust.ini. You can change this by giving the parameter on the commandline. For instance, to start Robust with HG parameters, one would use<br />
<br />
Robust.exe -inifile=Robust.HG.ini<br />
<br />
So if you wanted to run every connector apart from assets in one instance of ROBUST and the asset connector in another instance, you would have two ini files. One could remain Robust.ini and have<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector"<br />
GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector"<br />
GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector"<br />
AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector"<br />
OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector"<br />
AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector"<br />
LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector"<br />
PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector"<br />
UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector"<br />
GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector"<br />
FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector"<br />
MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector"<br />
MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"<br />
</source><br />
<br />
The other could be called Robust.Assets.ini and have<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
AssetServiceConnector = "8004/OpenSim.Server.Handlers.dll:AssetServiceConnector"<br />
</source><br />
<br />
Note that this is using port 8004 instead of port 8003. This is necessary since only one executable can use each port at a time. You will need to make sure your simulator configuration files use port 8004 for the asset service as well.<br />
<br />
You will also need to change the default network port to 8004 for this second copy of Robust.exe<br />
<br />
<pre><br />
[Network]<br />
port = 8004<br />
</pre><br />
<br />
Once you've created the two ROBUST configuration files (Robust.ini containing all services apart from asset and Robust.Assets.ini containing only the asset service), then you could start the first Robust.exe as usual.<br />
<br />
Robust.exe<br />
<br />
This will load Robust.ini, as we haven't specified a Robust.ini. Also, the logfile it will use will be Robust.log as we haven't manually specified one.<br />
<br />
The second ROBUST instance we would start with<br />
<br />
Robust.exe -inifile=Robust.Assets.ini -logfile=Robust.Assets.log<br />
<br />
The -inifile switch tells the second Robust instance to load it's configuration from Robust.Assets.ini rather than Robust.ini. The -logfile switch tells Robust.exe to use Robust.Assets.log as its logfile rather than the default Robust.log. If you don't specify this switch then you may see errors on the console about a locked log file.<br />
<br />
At this point you should have two running Robust.exe instances.<br />
<br />
If you put the ROBUST instances on different machines then don't forget to change the relevant service URIs in each simulator to match.<br />
<br />
Since OpenSimulator services are stateless (e.g. every request is unconnected with other requests as long as they affect the same persistent data where necessary), you can also load balance by starting more than one ROBUST instance with a copy of the same service (e.g. multiple asset services using the same database). Requests would be round-robined between the service copies using an HTTP reverse proxy implementation (e.g. nginx). See [[Performance#Services]] for more details.<br />
<br />
== Notes ==<br />
[http://opensimulator.org/pipermail/opensim-users/2012-October/011099.html Useful discussion about separating Robust instances]<br />
<br />
= Attaching your sim to someone else's grid =<br />
* Make sure you understand, agree and will enforce that grid Terms of Service<br />
* Follow the instructions of that grid owner!<br />
<br />
To set up the region server (i.e., <tt>OpenSim.exe</tt>) to connect to an external grid, follow the [[Configuration#Step 2: Configure an OpenSim.exe to use the ROBUST services]] instructions above.<br />
<br />
The grid will have already provided with the required services. In step 2 you will need to use the provided URLs for their services.<br />
<br />
In your bin/Regions.ini file (or other region config file) you will also need to set the grid co-ordinates to your regions provided from the grid operator. See [[Configuring Regions]] for more information.<br />
<br />
= Running an OpenSimulator standalone or grid installation with Hypergrid enabled =<br />
[[Hypergrid]] is an emerging architecture supported by OpenSimulator that allows a user with an account on one standalone or grid to visit other Hypergrid-enabled standalones or grids, and for users from those grids to visit the home grid. This does not require the two installations to share a central set of data services (assets, inventory, etc.). Please see [[Installing and Running Hypergrid]] for more details.<br />
<br />
= Further notes =<br />
<br />
== Troubleshooting ==<br />
<br />
See [[Troubleshooting]] <br />
<br />
== Running OpenSimulator in 32 bit in Windows 64bit==<br />
<br />
Windows 64Bit can run several instances of OpenSim in 32Bit mode, allowing each to use up to about 2GB of total physical ram.<br />
<br />
This is almost the total memory available on 32Bit versions of Windows, for all the programs (around 3GB)<br />
<br />
In 32Bit mode, the simulator uses less memory and may be a bit faster, so unless you have very large regions, you can ran it in 32Bit mode, just run:<br />
<br />
OpenSim32.exe instead of OpenSim.exe<br />
<br />
You can also run Robust in 32Bit mode:<br />
<br />
Robust32.exe in place of Robust.exe<br />
<br />
Linux 64Bit only runs the simulator in 64Bit mode<br />
<br />
== Increasing the stack reserve level when using OpenDynamicsEngine (old ODE and ubODE) on Linux or other Unix variants ==<br />
<br />
Increase your stack reserve level with the following command;<br />
<tt>ulimit -s 262144</tt> Or, run the opensim.sh to start up OpenSimulator.<br />
<br />
== Firewalls ==<br />
Some operation systems or distributions run their own firewall by default. If you can't access to OpenSimulator from remote client, you'll need to check their settings. See [[Firewall Settings]] for details.<br />
<br />
== Legacy Configuration Information ==<br />
These are some pages containing some legacy configuration information of unknown accuracy.<br />
<br />
[[OpenSim 0.6.6 legacy configuration information]]<br />
<br />
<br />
== Additional Optional Configuration Tasks ==<br />
<br />
=== Further configure OpenSimulator ===<br />
If you've looked through OpenSim.ini.example or any other of the config files, you'll see that there's a very large number of configurable parameters spread across multiple files. See [[Configuring Simulator Parameters]] for more details about the configuration infrastructure and how settings in identically named sections (e.g. [XEngine]) are merged by OpenSimulator on loading.<br />
<br />
=== Set up a second region to run on the same simulator ===<br />
See [[Configuring Regions]].<br />
<br />
=== Run Multiple Standalone Instances of OpenSimulator on the Same Server ===<br />
For each subsequent instance of OpenSim, change the 'http_listener_port' in [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] to the value excluding 9000, and 'InternalPort' in Regions.ini to the value excluding 9000. Also, make sure your regions are using different ports, as explained in [[Configuring Regions]].<br />
<br />
=== Load region content ===<br />
You can load content onto regions by using the [[OpenSim Archives|load oar command]]. To load individual OAR files into each region, use the 'change region [regionname]' command and then 'load oar [oar-location]'.<br />
<br />
=== OpenSim.exe command line options ===<br />
OpenSim.exe has command line options which allow you to perform actions such as reading configuration files from a different directory. See [[OpenSim.exe Command Line Options]] for more details.<br />
<br />
=== Script engine ===<br />
OpenSimulator supports multiple script engines. See [[ScriptEngines]] for details. If you don't know what this means then the default script engine will be fine. In fact, recent versions of OpenSimulator only ship with one script engine, the XEngine.<br />
<br />
=== Permissions Configuration ===<br />
OpenSimulator has a quite elaborate set of permissions. See [[Permissions (Server)]] for details. By default, permissions are active on region simulators.<br />
<br />
=== Logging ===<br />
By default, OpenSimulator logs information to a file called OpenSim.log in the bin directory. See [[Logging]] for details on how to further configure this if required.<br />
<br />
=== FSAsset Service ===<br />
By default the OpenSimulator asset service will store assets in the robust database. If you expect your asset data to grow larger than 50Gb you should consider changing to the [[FSAssets Service]]. (FSAssets is a new service and is currently only available in the latest development branch)<br />
<br />
=== Set up other optional modules ===<br />
* [[IRCBridgeModule]]<br />
* [[Freeswitch Module]]<br />
* [[Offline Messaging]]<br />
* [[Enabling Groups]]<br />
* See also [[User_Documentation#Setup]]<br />
<br />
=== Configuration of Web Server and Pages ===<br />
OpenSimulator contains a web server that can serve up a variety of pages. Some which come from external files and some are generated internally.<br />
* [[External Files]]<br />
* [[Internally Generated]]<br />
<br />
= Where to go from here =<br />
<br />
* [[NAT Loopback Routers]] Router and Nat Loopback Information<br />
<br />
* [[Upgrading]] from an old OpenSimulator version to a newer one.<br />
<br />
* [[Configuring_Simulator_Parameters]] cascading configuration structure, environment variables<br />
<br />
* [[Server Commands]] for creating users and controlling the system.<br />
<br />
= References =<br />
<br />
* [http://dist.opensimulator.org/wiki/opensim-standalone.odg OpenOffice draw file for OpenSimulator standalone diagram]<br />
* [http://dist.opensimulator.org/wiki/opensim-grid-simple.odg OpenOffice draw file for OpenSimulator grid diagram]<br />
<br />
[[Category:Configuration]]</div>Aiaustinhttp://opensimulator.org/wiki/LLUDP_DissectorLLUDP Dissector2023-01-14T11:39:26Z<p>Aiaustin: Add GitHub repository location for LLUSP Dissector</p>
<hr />
<div>__NOTOC__<br />
{{Quicklinks}}<br />
<br /><br />
<br />
== LLUDP protocol dissector ==<br />
On this page you will find the Lua code for a wireshark protocol dissector that can parse the message_template.msg file and use that information to decode all the message fields from the Linden UDP protocol. The code is also available on [https://github.com/Neopallium/lludp_dissector GitHub]<br />
<br />
== Installing ==<br />
* Requires wireshark with Lua 5.1.x support. [http://wiki.wireshark.org/Lua See this page for getting wireshark to support lua]<br />
<br />
=== on Linux ===<br />
* Copy all four source files into ~/.wireshark<br />
* edit ''/etc/wireshark/init.lua'' (or equivalent on your system) and change ''disable_lua'' to ''false'' (default is true)<br />
* If you need to run wireshark as the root user or using sudo then you will need to edit the scripts into one file by replacing the dofile("script.lua") calls with the contents of file between the quotes.<br />
* The other method is to add your user account to the correct group (on Gentoo it is group "wireshark") that will allow your non-root user to capture packets.<br />
<br />
=== on Windows ===<br />
* Copy all four source files into your user profiles directory <br />
<br />
'''Vista*'''<br />
<br />
C:\Users\<username>\AppData\Roaming\Wireshark <br />
<br />
'''XP/2000'''<br />
<br />
C:\Documents and Settings\<username>\Application Data\Wireshark<br />
<br />
* Edit C:\Program Files\Wireshark\init.lua and change ''disable_lua'' to ''false'' (default is true)<br />
<br />
* *Note: I have only tested this on Windows XP<br />
<br />
== LLUDP preferences ==<br />
There are three preferences that can be changed from wiresharks "Preferences" dialog:<br />
* Message template file: Full path to the message_template.msg file used to decode message name & details from the packets. On windows XP/Vista use double backslash '\\' instead of single blackslash '\' to separate directories (Example "C:\\Program Files\\SecondLife\\app_settings\\message_template.msg").<br />
* UDP port range start: First UDP port to mark as LLUDP packets. (default 13000)<br />
* UDP port range end: Last UDP port to mark as LLUDP packets. (default 13050)<br />
<br />
If your OpenSimulator regions are using ports 9000-9050 range then change the UDP port range.<br />
<br />
== Description of source files ==<br />
* "init.lua" -- simple script that loads the "lludp.lua" script.<br />
* "lludp.lua" -- contains the code that decodes each packet header and decompresses zero-encoded packets. This file uses wireshark only functions for accessing packet bytes and building a tree of information from each packet.<br />
* "llmessage.lua" -- contains the message_template.msg file parser the decodes the tokens from the lexer into an tree of tables containing all details about each message/block/variable from the template file. This file only has pure lua code.<br />
* "lexer.lua" -- contains the template file lexer. This lexer knows how to tokenize the template file into the follow tokens: IDENTIFIER, NUMBER, COMMENT, EOL. The stream of tokens produced by this lexer is parsed by the "llmessage.lua" file. This file only has pure lua code.<br />
<br />
=== Code license ===<br />
This Wireshark dissector maybe used under the terms of the "Simplified BSD License" or the [http://www.gnu.org/licenses/gpl.txt GPL]. -- Robert G. Jakabosky <bobby@sharedrealm.com><br />
<pre><br />
Simplified BSD License:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.<br />
<br />
</pre><br />
<br />
=== File "init.lua" ===<br />
<source lang="lua"><br />
<br />
-- register http to handle tcp ports 8000-8010 and 9000<br />
do<br />
local tcp_port_table = DissectorTable.get("tcp.port")<br />
local http_dissector = tcp_port_table:get_dissector(80)<br />
for port = 8000,8010 do<br />
tcp_port_table:add(port,http_dissector)<br />
end<br />
tcp_port_table:add(9000,http_dissector)<br />
end<br />
<br />
-- Load lludp protocol dissector.<br />
dofile("lludp.lua")<br />
<br />
</source><br />
<br />
=== File "lludp.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
dofile("llmessage.lua")<br />
<br />
-- cache globals to local for speed.<br />
local str_format=string.format<br />
<br />
-- test if ByteArray only has printable ASCII character and ends with '\0'<br />
local allowed_special = {<br />
[0] = true, -- null<br />
[9] = true, -- tab<br />
[10] = true, -- new line<br />
[13] = true, -- carriage return<br />
}<br />
local function is_string(bytes)<br />
local c<br />
local max = bytes:len() - 1<br />
for i=0,max do<br />
c = bytes:get_index(i)<br />
if c >= 127 then -- not ascii character<br />
return false<br />
elseif c < 32 then -- control character range<br />
if not allowed_special[c] then<br />
-- control characters between NULL and Space<br />
return false<br />
elseif c == 0 and i < max then<br />
-- null byte only allowed at end.<br />
return false<br />
end<br />
end<br />
end<br />
return true<br />
end<br />
<br />
-- lludp protocol example<br />
-- declare our protocol<br />
local lludp_proto = Proto("lludp","LLUDP","LindenLabs UDP Protocol")<br />
<br />
-- setup preferences<br />
lludp_proto.prefs["template_file"] =<br />
Pref.string("Message template file", "message_template.msg", "Message template file")<br />
lludp_proto.prefs["udp_port_start"] =<br />
Pref.string("UDP port range start", "13000", "First UDP port to decode as this protocol")<br />
lludp_proto.prefs["udp_port_end"] =<br />
Pref.string("UDP port range end", "13050", "Last UDP port to decode as this protocol")<br />
-- current preferences settings.<br />
local current_settings = {<br />
template_file = "",<br />
udp_port_start = -1,<br />
udp_port_end = -1,<br />
}<br />
-- current list of parsed messages.<br />
local message_details = nil<br />
<br />
-- setup protocol fields.<br />
lludp_proto.fields = {}<br />
local fds = lludp_proto.fields<br />
fds.flags = ProtoField.uint8("lludp.flags", "Flags", base.HEX, nil, 0xFF)<br />
fds.flags_zero = ProtoField.uint8("lludp.flags.zero", "Zero", base.HEX, nil, 0x80)<br />
fds.flags_reliable = ProtoField.uint8("lludp.flags.rel", "Reliable", base.HEX, nil, 0x40)<br />
fds.flags_resent = ProtoField.uint8("lludp.flags.res", "Resent", base.HEX, nil, 0x20)<br />
fds.flags_ack = ProtoField.uint8("lludp.flags.ack", "Ack", base.HEX, nil, 0x10)<br />
fds.sequence = ProtoField.uint32("lludp.sequence", "Sequence", base.DEC)<br />
fds.extra_len = ProtoField.uint8("lludp.extra_len", "Extra length", base.DEC)<br />
fds.extra_bytes = ProtoField.bytes("lludp.extra_bytes", "Extra header", base.HEX)<br />
fds.msg_id = ProtoField.uint32("lludp.msg.id", "Message ID", base.HEX)<br />
fds.msg_name = ProtoField.bytes("lludp.msg.name", "Message name")<br />
fds.msg = ProtoField.bytes("lludp.msg", "Message body", base.HEX)<br />
fds.acks_count = ProtoField.uint8("lludp.acks_count", "Acks count", base.DEC)<br />
fds.acks = ProtoField.uint32("lludp.acks", "Acks", base.DEC)<br />
fds.block_count = ProtoField.uint8("lludp.block_count", "Block count", base.DEC)<br />
fds.block = ProtoField.bytes("lludp.block", "Block", base.HEX)<br />
fds.var_fixed = ProtoField.bytes("lludp.var.fixed", "Fixed blob", base.HEX)<br />
fds.var_variable = ProtoField.bytes("lludp.var.variable", "Variable blob", base.HEX)<br />
fds.var_string = ProtoField.stringz("lludp.var.string", "String")<br />
fds.var_u8 = ProtoField.uint8("lludp.var.u8", "U8", base.DEC)<br />
fds.var_u16 = ProtoField.uint16("lludp.var.u16", "U16", base.DEC)<br />
fds.var_u32 = ProtoField.uint32("lludp.var.u32", "U32", base.DEC)<br />
fds.var_u64 = ProtoField.uint64("lludp.var.u64", "U64", base.DEC)<br />
fds.var_s8 = ProtoField.int8("lludp.var.s8", "S8", base.DEC)<br />
fds.var_s16 = ProtoField.int16("lludp.var.s16", "S16", base.DEC)<br />
fds.var_s32 = ProtoField.int32("lludp.var.s32", "S32", base.DEC)<br />
fds.var_s64 = ProtoField.int64("lludp.var.s64", "S64", base.DEC)<br />
fds.var_f32 = ProtoField.float("lludp.var.f32", "F32", base.DEC)<br />
fds.var_f64 = ProtoField.double("lludp.var.f64", "F64", base.DEC)<br />
fds.var_llvector3 = ProtoField.bytes("lludp.var.llvector3", "LLVector3", base.HEX)<br />
fds.var_llvector3d = ProtoField.bytes("lludp.var.llvector3d", "LLVector3d", base.HEX)<br />
fds.var_llvector4 = ProtoField.bytes("lludp.var.llvector4", "LLVector4", base.HEX)<br />
fds.var_llquaternion = ProtoField.bytes("lludp.var.llquaternion", "LLQuaternion", base.HEX)<br />
fds.var_lluuid = ProtoField.bytes("lludp.var.lluuid", "LLUUID", base.HEX)<br />
fds.var_bool = ProtoField.uint8("lludp.var.bool", "BOOL", base.DEC)<br />
fds.var_ipaddr = ProtoField.ipv4("lludp.var.ipaddr", "IPADDR", base.DEC)<br />
fds.var_ipport = ProtoField.uint16("lludp.var.ipport", "IPPORT", base.DEC)<br />
-- variable type handlers.<br />
local variable_handlers = {<br />
Fixed = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_fixed, rang)<br />
if len <= 4 then<br />
ti:set_text(str_format("%s: 0x%08x",var.name, rang:uint()))<br />
else<br />
ti:set_text(str_format("%s: length=%d, Blob:%s", var.name, len, tostring(rang)))<br />
end<br />
end,<br />
Variable = function(block_tree, buffer, offset, len, var)<br />
local is_data = false<br />
-- try to guess if this field is text.<br />
if var.name:find("Data") then<br />
-- this is a data find.<br />
is_data = true<br />
end<br />
local str_rang = buffer(offset + var.count_length, len - var.count_length)<br />
local bytes = str_rang:bytes()<br />
if not is_data and is_string(bytes) then<br />
local str = str_rang:string()<br />
local ti = block_tree:add(fds.var_string, buffer(offset, len))<br />
ti:set_text(str_format("%s: %s", var.name, str))<br />
else<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_variable, rang)<br />
if len <= 4 then<br />
ti:set_text(str_format("%s: 0x%08x",var.name, rang:uint()))<br />
else<br />
ti:set_text(str_format("%s: length=%d, Blob:%s", var.name, len, tostring(rang)))<br />
end<br />
end<br />
end,<br />
U8 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u8, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U16 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u16, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u32, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:le_uint()))<br />
end,<br />
U64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_u64, rang)<br />
ti:set_text(str_format("%s: 0x%s",var.name, tostring(rang)))<br />
end,<br />
S8 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s8, rang)<br />
local num = rang:le_uint()<br />
if num > 127 then num = num - 256 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S16 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s16, rang)<br />
local num = rang:le_uint()<br />
if num > 32768 then num = num - 65536 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s32, rang)<br />
local num = rang:le_uint()<br />
if num > 2147483648 then num = num - 4294967296 end<br />
ti:set_text(str_format("%s: %d",var.name, num))<br />
end,<br />
S64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_s64, rang)<br />
ti:set_text(str_format("%s: 0x%s",var.name, tostring(rang)))<br />
end,<br />
F32 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_f32, rang)<br />
ti:set_text(str_format("%s: %f", var.name, rang:le_float()))<br />
end,<br />
F64 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_f64, rang)<br />
ti:set_text(str_format("%s: %f", var.name, rang:le_float()))<br />
end,<br />
LLVector3 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector3, rang)<br />
-- parse LLVector3<br />
local x,y,z<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f>", var.name, x, y, z))<br />
end,<br />
LLVector3d = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector3d, rang)<br />
-- parse LLVector3d<br />
local x,y,z<br />
x = buffer(offset + 0,8):le_float()<br />
y = buffer(offset + 8,8):le_float()<br />
z = buffer(offset + 16,8):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f>", var.name, x, y, z))<br />
end,<br />
LLVector4 = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llvector4, rang)<br />
-- parse LLVector4<br />
local x,y,z,s<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
s = buffer(offset + 12,4):le_float()<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f,%f>", var.name, x, y, z, s))<br />
end,<br />
LLQuaternion = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_llquaternion, rang)<br />
-- parse LLQuaternion<br />
local x,y,z,w<br />
x = buffer(offset + 0,4):le_float()<br />
y = buffer(offset + 4,4):le_float()<br />
z = buffer(offset + 8,4):le_float()<br />
-- calculate W<br />
w = 1 - (x * x) - (y * y) - (z * z)<br />
if w > 0 then<br />
w = math.sqrt(w)<br />
else<br />
w = 0<br />
end<br />
-- display<br />
ti:set_text(str_format("%s: <%f,%f,%f,%f>", var.name, x, y, z, w))<br />
end,<br />
LLUUID = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_lluuid, rang)<br />
local str = tostring(rang)<br />
str = str:sub(1,8) .. '-' ..<br />
str:sub(9,12) .. '-' .. str:sub(13,16) .. '-' ..<br />
str:sub(17,20) .. '-' .. str:sub(21)<br />
ti:set_text(str_format("%s: %s", var.name, str))<br />
end,<br />
BOOL = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add_le(fds.var_bool, rang)<br />
local val = "false"<br />
if rang:le_uint() > 0 then<br />
val = "true"<br />
end<br />
ti:set_text(str_format("%s: %s", var.name, val))<br />
end,<br />
IPADDR = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add(fds.var_ipaddr, rang)<br />
ti:set_text(str_format("%s: %s", var.name, tostring(rang:ipv4())))<br />
end,<br />
IPPORT = function(block_tree, buffer, offset, len, var)<br />
local rang = buffer(offset, len)<br />
local ti = block_tree:add(fds.var_ipport, rang)<br />
ti:set_text(str_format("%s: %d", var.name, rang:uint()))<br />
end,<br />
}<br />
<br />
-- un-register lludp to handle udp port range<br />
local function unregister_udp_port_range(start_port, end_port)<br />
if not start_port or start_port <= 0 or not end_port or end_port <= 0 then<br />
return<br />
end<br />
udp_port_table = DissectorTable.get("udp.port")<br />
for port = start_port,end_port do<br />
udp_port_table:remove(port,lludp_proto)<br />
end<br />
end<br />
<br />
-- register lludp to handle udp port range<br />
local function register_udp_port_range(start_port, end_port)<br />
if not start_port or start_port <= 0 or not end_port or end_port <= 0 then<br />
return<br />
end<br />
udp_port_table = DissectorTable.get("udp.port")<br />
for port = start_port,end_port do<br />
udp_port_table:add(port,lludp_proto)<br />
end<br />
end<br />
<br />
-- handle preferences changes.<br />
function lludp_proto.init(arg1, arg2)<br />
local old_start, old_end<br />
local new_start, new_end<br />
-- check if preferences have changed.<br />
for pref_name,old_v in pairs(current_settings) do<br />
local new_v = lludp_proto.prefs[pref_name]<br />
if new_v ~= old_v then<br />
if pref_name == "template_file" then<br />
-- load & parse message_template.msg file.<br />
local file = new_v<br />
if file and file:len() > 0 then<br />
local new_details = parse_template(file)<br />
if new_details then<br />
message_details = new_details<br />
end<br />
end<br />
elseif pref_name == "udp_port_start" then<br />
old_start = old_v<br />
new_start = new_v<br />
elseif pref_name == "udp_port_end" then<br />
old_end = old_v<br />
new_end = new_v<br />
end<br />
-- save new value.<br />
current_settings[pref_name] = new_v<br />
end<br />
end<br />
-- un-register old port range<br />
if old_start and old_end then<br />
unregister_udp_port_range(tonumber(old_start), tonumber(old_end))<br />
end<br />
-- register new port range.<br />
if new_start and new_end then<br />
register_udp_port_range(tonumber(new_start), tonumber(new_end))<br />
end<br />
end<br />
<br />
-- parse flag bits.<br />
local FLAG_ZER = 4<br />
local FLAG_REL = 3<br />
local FLAG_RES = 2<br />
local FLAG_ACK = 1<br />
local flag_names = {"ACK", "RES", "REL", "ZER"}<br />
local bits_lookup = {<br />
{},<br />
{1},<br />
{2},<br />
{2,1},<br />
{3},<br />
{3,1},<br />
{3,2},<br />
{3,2,1},<br />
{4},<br />
{4,1},<br />
{4,2},<br />
{4,2,1},<br />
{4,3},<br />
{4,3,1},<br />
{4,3,2},<br />
{4,3,2,1},<br />
}<br />
local function parse_flags(flags)<br />
flags = (flags / 16) + 1<br />
local bit_list = bits_lookup[flags]<br />
local bits = {}<br />
local names = ""<br />
for _, bit in ipairs(bit_list) do<br />
bits[bit] = true<br />
if names:len() > 0 then<br />
names = names .. ", "<br />
end<br />
names = names .. flag_names[bit]<br />
end<br />
return bits, names<br />
end<br />
<br />
local function grow_buff(buff, size)<br />
local old_size = buff:len()<br />
if old_size > size then return end<br />
-- buffer needs to grow<br />
buff:set_size(size)<br />
-- fill new space with zeros<br />
for i = old_size,size-1 do<br />
buff:set_index(i, 0)<br />
end<br />
end<br />
<br />
local function zero_decode(zero_buf)<br />
local out_buf = ByteArray.new()<br />
local zero_off = 0<br />
local zero_len = zero_buf:len()<br />
local out_size = 0<br />
local out_off = 0<br />
local b<br />
-- pre-allocate<br />
grow_buff(out_buf, zero_len)<br />
out_size = zero_len<br />
-- zero expand<br />
repeat<br />
b = zero_buf:get_index(zero_off)<br />
if b == 0 then<br />
-- get zero count<br />
local count = zero_buf:get_index(zero_off + 1)<br />
if count == 0 then count = 255 end<br />
out_off = out_off + count<br />
-- fill zeros<br />
if out_off > out_size then<br />
out_size = out_off + 128<br />
grow_buff(out_buf, out_size)<br />
end<br />
zero_off = zero_off + 2<br />
else<br />
if out_off >= out_size then<br />
out_size = out_off + (zero_len - zero_off) + 4<br />
grow_buff(out_buf, out_size)<br />
end<br />
-- copy non-zero bytes.<br />
out_buf:set_index(out_off,b)<br />
zero_off = zero_off + 1<br />
out_off = out_off + 1<br />
end<br />
until zero_off == zero_len<br />
-- truncate to real size.<br />
out_buf:set_size(out_off)<br />
<br />
return out_buf:tvb("Decompressed Data")<br />
end<br />
<br />
local function parse_msg_id(buff)<br />
local b = buff:get_index(0)<br />
local msg_id = b<br />
local msg_id_len = 1<br />
if b == 255 then<br />
b = buff:get_index(1)<br />
msg_id = msg_id * 256 + b<br />
msg_id_len = 2<br />
if b == 255 then<br />
b = buff:get_index(2)<br />
msg_id = msg_id * 256 + b<br />
b = buff:get_index(3)<br />
msg_id = msg_id * 256 + b<br />
msg_id_len = 4<br />
end<br />
end<br />
return msg_id, msg_id_len<br />
end<br />
<br />
-- get message name.<br />
local function get_msg_name(msg_id)<br />
-- check that we have message details<br />
if message_details == nil then<br />
return str_format("0x%08x", msg_id)<br />
end<br />
-- find message name from id.<br />
local msg = message_details.msgs[msg_id]<br />
-- Invalid message id<br />
if msg == nil then<br />
return str_format("0x%08x", msg_id)<br />
end<br />
return msg.name<br />
end<br />
<br />
-- calculate length a block.<br />
local function get_block_length(msg_buffer, start_offset, block)<br />
-- check if bock is fixed length.<br />
if block.fixed_length then<br />
return block.min_length<br />
end<br />
-- parse block's variables to calculate total block length.<br />
local offset = start_offset<br />
local rang<br />
for _,var in ipairs(block) do<br />
local len = 0<br />
if var.has_count then<br />
-- variable with length bytes.<br />
len = var.count_length<br />
--print(var.name, offset, ", len:", len)<br />
rang = msg_buffer(offset, len)<br />
len = len + rang:le_uint()<br />
--print(var.name, var.count_length, ", total:", len)<br />
else<br />
-- fixed length variable<br />
len = var.length<br />
--print(var.name, ", total:", len)<br />
end<br />
offset = offset + len<br />
end<br />
return (offset - start_offset)<br />
end<br />
<br />
-- build block tree<br />
local function build_block_tree(msg_buffer, block_tree, start_offset, block)<br />
local offset = start_offset<br />
local rang<br />
-- parse block's variables<br />
for _,var in ipairs(block) do<br />
local len = 0<br />
if var.has_count then<br />
-- variable with length bytes.<br />
len = var.count_length<br />
rang = msg_buffer(offset, len)<br />
len = len + rang:le_uint()<br />
else<br />
-- fixed length variable<br />
len = var.length<br />
end<br />
-- get variable's type field.<br />
local handler = variable_handlers[var.type]<br />
-- parse variable.<br />
if handler then<br />
handler(block_tree, msg_buffer, offset, len, var)<br />
end<br />
offset = offset + len<br />
end<br />
return (offset - start_offset)<br />
end<br />
<br />
-- buid message tree<br />
local function build_msg_tree(msg_buffer, msg_tree, msg_id)<br />
local offset = 0<br />
local rang<br />
-- check that we have message details<br />
if message_details == nil then<br />
msg_tree:set_text(str_format("Message Id: 0x%08x", msg_id))<br />
return nil<br />
end<br />
-- find message name from id.<br />
local msg = message_details.msgs[msg_id]<br />
-- Invalid message id<br />
if msg == nil then<br />
msg = str_format("Invalid message id: 0x%08x", msg_id)<br />
msg_tree:add_expert_info(PI_MALFORMED, PI_ERROR, msg)<br />
msg_tree:set_text(msg)<br />
return nil<br />
end<br />
-- skip message id bytes.<br />
offset = msg.id_length<br />
-- set message name.<br />
msg_tree:set_text(msg.name .. ":")<br />
-- proccess message blocks<br />
for _,block in ipairs(msg) do<br />
local count = block.count<br />
if count == nil then<br />
-- parse count byte.<br />
rang = msg_buffer(offset,1)<br />
count = rang:uint()<br />
msg_tree:add(fds.block_count,rang)<br />
offset = offset + 1<br />
end<br />
-- print("block name: ", block.name, count)<br />
for n=1,count do<br />
local block_len = get_block_length(msg_buffer, offset, block)<br />
-- parse block<br />
rang = msg_buffer(offset, block_len)<br />
local block_tree = msg_tree:add(fds.block,rang)<br />
if count > 1 then<br />
block_tree:set_text(str_format("%s: %d of %d",block.name,n,count))<br />
else<br />
block_tree:set_text(block.name)<br />
end<br />
-- parse block variables.<br />
build_block_tree(msg_buffer, block_tree, offset, block)<br />
offset = offset + block_len<br />
end<br />
end<br />
return msg.name<br />
end<br />
<br />
-- packet dissector<br />
function lludp_proto.dissector(buffer,pinfo,tree)<br />
local rang,offset<br />
pinfo.cols.protocol = "LLUDP"<br />
local lludp_tree = tree:add(lludp_proto,buffer(),"Linden UDP Protocol")<br />
-- Flags byte.<br />
offset = 0<br />
rang = buffer(offset,1)<br />
local flags = rang:uint()<br />
local flags_bits, flags_list = parse_flags(flags)<br />
flags_tree = lludp_tree:add(fds.flags, rang)<br />
flags_tree:set_text("Flags: " .. str_format('0x%02X (%s)', flags, flags_list))<br />
flags_tree:add(fds.flags_zero, rang)<br />
flags_tree:add(fds.flags_reliable, rang)<br />
flags_tree:add(fds.flags_resent, rang)<br />
flags_tree:add(fds.flags_ack, rang)<br />
offset = offset + 1<br />
-- Sequence number 4 bytes.<br />
rang = buffer(offset,4)<br />
local sequence = rang:uint()<br />
lludp_tree:add(fds.sequence, rang)<br />
offset = offset + 4<br />
-- Extra header length.<br />
rang = buffer(offset,1)<br />
local extra_length = rang:uint()<br />
lludp_tree:add(fds.extra_len,rang)<br />
offset = offset + 1<br />
-- Extra header data.<br />
if extra_length > 0 then<br />
rang = buffer(offset, extra_length)<br />
lludp_tree:add(fds.extra_bytes, rang)<br />
offset = offset + extra_length<br />
end<br />
-- Appended Acks. count<br />
local acks_bytes = 0<br />
local acks_count = 0<br />
if flags_bits[FLAG_ACK] then<br />
rang = buffer(buffer:len() - 1, 1)<br />
acks_count = rang:uint()<br />
acks_bytes = (acks_count * 4) + 1<br />
end<br />
-- Zero Decode<br />
local msg_len = (buffer:len() - acks_bytes) - offset<br />
if flags_bits[FLAG_ZER] then<br />
msg_buffer=zero_decode(buffer(offset,msg_len):bytes())<br />
msg_len = msg_buffer:len()<br />
offset = 0<br />
else<br />
msg_buffer = buffer(offset, msg_len):tvb()<br />
offset = 0<br />
end<br />
-- Message ID<br />
local msg_id, msg_id_len = -1, 4<br />
if msg_id_len > msg_len then<br />
msg_id_len = msg_len<br />
end<br />
msg_id, msg_id_len = parse_msg_id(msg_buffer(offset, msg_id_len):bytes())<br />
rang = msg_buffer(offset, msg_id_len)<br />
local msg_id_tree = lludp_tree:add(fds.msg_id, rang)<br />
local msg_name = get_msg_name(msg_id)<br />
if msg_name == nil then<br />
msg_id_tree:set_text(str_format("Message name: 0x%08x", msg_id))<br />
else<br />
msg_id_tree:set_text(str_format("Message name: %s",msg_name))<br />
end<br />
-- Message body.<br />
rang = msg_buffer(offset, msg_len)<br />
local msg_tree = lludp_tree:add(fds.msg, rang)<br />
build_msg_tree(msg_buffer, msg_tree, msg_id)<br />
-- Appended Acks. list.<br />
if flags_bits[FLAG_ACK] then<br />
local acks_off = buffer:len()<br />
rang = buffer(acks_off - 1, 1)<br />
acks_off = acks_off - acks_bytes<br />
local acks_tree = lludp_tree:add(fds.acks_count, rang)<br />
for i = 1,acks_count do<br />
rang = buffer(acks_off,4)<br />
acks_tree:add(fds.acks, rang)<br />
acks_off = acks_off + 4<br />
end<br />
end<br />
-- Info column<br />
pinfo.cols.info = str_format('[%s] Seq=%u Type=%s', flags_list, sequence, msg_name)<br />
end<br />
<br />
-- register lludp to handle udp ports 9000-9003<br />
register_udp_port_range(9000,9003)<br />
</source><br />
<br />
=== File "llmessage.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
dofile("lexer.lua")<br />
<br />
local lexer<br />
local cur_token = nil<br />
local cur_token_str = nil<br />
<br />
local function get_token(skip_tokens)<br />
repeat<br />
token = lexer.get_token()<br />
if token ~= nil then<br />
cur_token = token[1]<br />
cur_token_str = token[2]<br />
end<br />
until token == nil or not skip_tokens[cur_token]<br />
return token<br />
end<br />
<br />
local function run_parser(parser)<br />
local state = parser.init()<br />
if parser.skip_tokens == nil then parser.skip_tokens = {} end<br />
while get_token(parser.skip_tokens) do<br />
-- check what the parser is expecting next.<br />
if state.expect then<br />
-- check expected type<br />
if state.expect ~= cur_token then<br />
error(string.format("state.expected token '%s' instead of '%s'",<br />
TokenNames[state.expect], TokenNames[cur_token]))<br />
end<br />
-- reset expect field<br />
state.expect = nil<br />
end<br />
if state.expect_str then<br />
-- check expected string<br />
if state.expect_str ~= cur_token_str then<br />
error(string.format("state.expected token '%s' instead of '%s'",<br />
state.expect_str, cur_token_str))<br />
end<br />
-- reset expect_str field<br />
state.expect_str = nil<br />
end<br />
-- get handler function for current token.<br />
local f = parser[cur_token]<br />
if f ~= nil then<br />
local ret = f(state)<br />
if ret then<br />
-- praser finished.<br />
return ret<br />
end<br />
elseif parser.unhandled_error then<br />
error(string.format("unhandled token '%s' when paring '%s'\n", cur_token_str, parser.name))<br />
end<br />
end<br />
return parser.eof(state)<br />
end<br />
<br />
-- Known variable types and there fixed length.<br />
-- length == -1, requires a number after the type that is the length of count field<br />
-- length == -2, requires a number after the type that is the fixed variable length.<br />
VariableTypes = {<br />
Null = 0,<br />
Fixed = -2,<br />
Variable = -1,<br />
U8 = 1,<br />
U16 = 2,<br />
U32 = 4,<br />
U64 = 8,<br />
S8 = 1,<br />
S16 = 2,<br />
S32 = 4,<br />
S64 = 8,<br />
F32 = 4,<br />
F64 = 8,<br />
LLVector3 = 12,<br />
LLVector3d = 24,<br />
LLVector4 = 16,<br />
LLQuaternion = 12,<br />
LLUUID = 16,<br />
BOOL = 1,<br />
IPADDR = 4,<br />
IPPORT = 2,<br />
}<br />
<br />
--<br />
-- Variable parser<br />
--<br />
local variable_parser = {<br />
name = "variable",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
return {<br />
name = "<MISSING VARIABLE NAME>",<br />
type = "Null",<br />
has_count = false,<br />
length = 0,<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
required = 2<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "type"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "type" then<br />
state.type = cur_token_str<br />
state.length = VariableTypes[state.type]<br />
if state.length == nil then<br />
error("Unknown variable type: " .. cur_token_str)<br />
elseif state.length == -1 or state.length == -2 then<br />
state.expect = Token.NUMBER<br />
else<br />
state.required = state.required - 1<br />
end<br />
else<br />
error(string.format("unhandled variable identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "type" then<br />
if state.length == -1 then<br />
-- variable field length uses embedded count field<br />
state.has_count = true<br />
state.count_length = tonumber(cur_token_str)<br />
state.length = nil<br />
elseif state.length == -2 then<br />
-- fixed field length<br />
state.length = tonumber(cur_token_str)<br />
end<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled variable number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
error("sub block not allowed in variable block")<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of variable: " .. state.name)<br />
end,<br />
}<br />
<br />
-- Block Quantities<br />
local BlockQuantity = {<br />
Single = 1,<br />
Variable = -1,<br />
Multiple = -2,<br />
}<br />
<br />
--<br />
-- Block parser<br />
--<br />
local block_parser = {<br />
name = "block",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
return {<br />
name = "<MISSING BLOCK NAME>",<br />
quantity = "Single",<br />
count = 0,<br />
min_length = 0,<br />
fixed_length = true,<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
required = 2<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "quantity"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "quantity" then<br />
state.quantity = cur_token_str<br />
state.count = BlockQuantity[cur_token_str]<br />
if state.count == nil then<br />
error("Unknown block quantity: " .. cur_token_str)<br />
elseif state.count == -2 then<br />
state.expect_field = "count"<br />
state.expect = Token.NUMBER<br />
else<br />
if state.count == -1 then<br />
state.has_count = true<br />
state.count_length = 1<br />
state.count = nil<br />
end<br />
state.required = state.required - 1<br />
end<br />
else<br />
error(string.format("unhandled block identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "count" then<br />
state.count = tonumber(cur_token_str)<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled block number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local variable = run_parser(variable_parser)<br />
table.insert(state,variable)<br />
-- add length of fixed length variables to minimal length of block.<br />
if variable.has_count then<br />
state.min_length = state.min_length + variable.count_length<br />
state.fixed_length = false<br />
else<br />
state.min_length = state.min_length + variable.length<br />
end<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of block: " .. state.name)<br />
end,<br />
}<br />
<br />
--<br />
-- Message parser<br />
--<br />
local message_parser = {<br />
name = "message",<br />
unhandled_error = false,<br />
skip_tokens = {[Token.EOL] = true},<br />
init = function()<br />
-- create state<br />
return {<br />
name = "<MISSING MESSAGE NAME>",<br />
expect = Token.IDENTIFIER,<br />
expect_field = "name",<br />
fixed_length = true,<br />
min_length = 0,<br />
required = 5<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
if state.expect_field == "name" then<br />
state.name = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "frequency"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "frequency" then<br />
state.frequency = cur_token_str<br />
state.expect = Token.NUMBER<br />
state.required = state.required - 1<br />
elseif state.expect_field == "trust" then<br />
state.trust = cur_token_str<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "compression"<br />
state.required = state.required - 1<br />
elseif state.expect_field == "compression" then<br />
state.compression = cur_token_str<br />
state.required = state.required - 1<br />
else<br />
error(string.format("unhandled message identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
if state.expect_field == "frequency" then<br />
state.number = tonumber(cur_token_str)<br />
state.expect = Token.IDENTIFIER<br />
state.expect_field = "trust"<br />
state.required = state.required - 1<br />
-- create true message id from frequency and message number<br />
local freq = state.frequency<br />
if freq == "High" then<br />
-- High is already correct.<br />
state.id = state.number<br />
state.id_length = 1<br />
elseif freq == "Medium" then<br />
state.id = tonumber("0xFF" .. string.format("%02X", state.number))<br />
state.id_length = 2<br />
elseif freq == "Low" then<br />
state.id = tonumber("0xFFFF" .. string.format("%04X", state.number))<br />
state.id_length = 4<br />
else<br />
-- Fixed is already correct.<br />
state.id = state.number<br />
state.id_length = 4<br />
end<br />
else<br />
error(string.format("unhandled message number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local block = run_parser(block_parser)<br />
table.insert(state,block)<br />
-- add min length of block to minimal length of message<br />
local min_length = block.min_length<br />
if block.has_count then<br />
-- add one byte for the block count<br />
min_length = min_length + 1<br />
state.fixed_length = false<br />
else<br />
-- if block is not fixed length then message can't be fixed length.<br />
if not block.fixed_length then<br />
state.fixed_length = false<br />
end<br />
min_length = min_length * block.count<br />
end<br />
state.min_length = state.min_length + min_length<br />
end,<br />
["}"] = function(state)<br />
if state.required > 0 then<br />
error("missing " .. state.required .. " fields")<br />
end<br />
-- clean state.<br />
state.required = nil<br />
state.expect = nil<br />
state.expect_field = nil<br />
return state<br />
end,<br />
eof = function(state)<br />
error("missing '}' token at end of message: " .. state.name)<br />
end,<br />
}<br />
<br />
--<br />
-- Template file parser<br />
--<br />
local template_parser = {<br />
name = "message_template",<br />
unhandled_error = false,<br />
init = function()<br />
return {<br />
version = 0,<br />
msg_count = 0,<br />
msgs = {}<br />
}<br />
end,<br />
[Token.IDENTIFIER] = function(state)<br />
-- handle version<br />
if cur_token_str == "version" then<br />
state.expect = Token.NUMBER<br />
state.last_ident = cur_token_str<br />
else<br />
error(string.format("unknown template identifier: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
[Token.NUMBER] = function(state)<br />
-- handle version number<br />
if state.last_ident == "version" then<br />
state.last_ident = nil<br />
state.version = tonumber(cur_token_str)<br />
-- check version number<br />
if state.version ~= 2 then<br />
error("invalid verion: " .. state.version)<br />
end<br />
else<br />
error(string.format("unhandled template number: %s\n",cur_token_str))<br />
end<br />
return nil<br />
end,<br />
["{"] = function(state)<br />
local message = run_parser(message_parser)<br />
state.msg_count = state.msg_count + 1<br />
state.msgs[message.id] = message<br />
end,<br />
["}"] = function(state)<br />
error(string.format("unhandled '%s' token",cur_token_str))<br />
end,<br />
eof = function(state)<br />
return state<br />
end,<br />
}<br />
<br />
function parse_template(file)<br />
-- create lexer<br />
local status, ret = pcall(get_lexer,file)<br />
if not status then<br />
ret = string.format("LLUDP: Failed parse file into tokens: %s\n%s\n", file, ret)<br />
error(ret, 0)<br />
return nil<br />
end<br />
lexer = ret<br />
-- parse template file<br />
local status, ret = pcall(run_parser,template_parser)<br />
if not status then<br />
ret = string.format("LLUDP: Failed parsing on line %s:%d: '%s'\n%s\n",<br />
file, lexer.get_line_number(), lexer.get_line(), ret)<br />
error(ret, 0)<br />
return nil<br />
end<br />
io.write("finished parsing: " .. file .. "\n")<br />
-- return list of messages parsed from file.<br />
return ret<br />
end<br />
<br />
--parse_template("message_template.msg")<br />
--print_tokens("message_template.msg")<br />
<br />
</source><br />
<br />
=== File "lexer.lua" ===<br />
<source lang="lua"><br />
-- [[BSD-Licensed:<br />
Copyright (c)2011, Robert G. Jakabosky <bobby@sharedrealm.com>. All rights reserved.<br />
<br />
Redistribution and use in source and binary forms, with or without modification, are<br />
permitted provided that the following conditions are met:<br />
<br />
1. Redistributions of source code must retain the above copyright notice, this list of<br />
conditions and the following disclaimer.<br />
<br />
2. Redistributions in binary form must reproduce the above copyright notice, this list<br />
of conditions and the following disclaimer in the documentation and/or other materials<br />
provided with the distribution.<br />
<br />
THIS SOFTWARE IS PROVIDED BY "Robert G. Jakabosky" ''AS IS'' AND ANY EXPRESS OR IMPLIED<br />
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br />
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "Robert G. Jakabosky" OR<br />
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.]] <br />
<br />
-- Token types.<br />
Token = {<br />
NONE = -1,<br />
IDENTIFIER = -2,<br />
NUMBER = -3,<br />
COMMENT = -6,<br />
EOL = -7,<br />
["{"] = "{",<br />
["}"] = "}",<br />
}<br />
TokenNames = {<br />
[-1] = "NONE",<br />
[-2] = "IDENTIFIER",<br />
[-3] = "NUMBER",<br />
[-6] = "COMMENT",<br />
[-7] = "EOL",<br />
["{"] = "{",<br />
["}"] = "}",<br />
}<br />
<br />
-- parse line into array of tokens.<br />
local function default_parse_tokens(line)<br />
local tokens = {}<br />
local comment = nil<br />
-- check for a comment on this line.<br />
local idx = line:find("//")<br />
if idx ~= nil then<br />
comment = {Token.COMMENT, line:sub(idx)}<br />
-- remove comment from line<br />
line = line:sub(1,idx - 1)<br />
end<br />
<br />
-- split line into tokens using white-space as token delimitator<br />
for tok in line:gmatch("%s?([^%s]+)") do<br />
local tok_type = Token.NONE<br />
-- check for number<br />
if tonumber(tok) ~= nil then<br />
tok_type = Token.NUMBER<br />
elseif Token[tok] then<br />
-- token is same as type<br />
tok_type = Token[tok]<br />
else<br />
-- token is an identifier<br />
tok_type = Token.IDENTIFIER<br />
end<br />
table.insert(tokens,{tok_type,tok})<br />
end<br />
-- insert comment token.<br />
if comment ~= nil then<br />
table.insert(tokens,comment)<br />
end<br />
-- add token to mark the end of this line<br />
table.insert(tokens,{Token.EOL, ""})<br />
return tokens<br />
end<br />
<br />
function get_lexer(file, parse_tokens)<br />
-- use the default line tokenizer if one is not provided<br />
if parse_tokens == nil then parse_tokens = default_parse_tokens end<br />
-- next/current line code<br />
local line_num = 0<br />
local line = nil<br />
local next_line = io.lines(file)<br />
-- parse line tokens code<br />
local get_next_token = nil<br />
local next_tokens = function ()<br />
local f, tokens, idx<br />
repeat<br />
line_num = line_num + 1<br />
line = next_line()<br />
if line == nil then return nil end<br />
tokens = parse_tokens(line)<br />
until tokens ~= nil<br />
-- create get_next_toekn function from table iterator<br />
f, tokens, idx = ipairs(tokens)<br />
get_next_token = function()<br />
idx, token = f(tokens, idx)<br />
return token<br />
end<br />
return tokens<br />
end<br />
<br />
-- get first group of tokens<br />
if next_tokens() == nil then<br />
-- error reading file or empty file<br />
return nil<br />
end<br />
-- build lexer table.<br />
local lexer = {<br />
get_token = function ()<br />
local token<br />
repeat<br />
token = get_next_token()<br />
if token == nil then<br />
-- get next group of tokens<br />
if next_tokens() == nil then<br />
-- end of file.<br />
return nil<br />
end<br />
end<br />
until token ~= nil<br />
return token<br />
end,<br />
get_line_number = function() return line_num end,<br />
get_line = function() return line end<br />
}<br />
return lexer<br />
end<br />
<br />
function print_tokens(file)<br />
local lexer = get_lexer(file)<br />
local num = -1<br />
while true do<br />
local tok = lexer.get_token()<br />
if tok == nil then<br />
break<br />
end<br />
if num ~= lexer.get_line_number() then<br />
num = lexer.get_line_number()<br />
io.write("\n")<br />
io.write(string.format("%d: ",num))<br />
end<br />
io.write(string.format("%s ",tok[2]))<br />
end<br />
io.write("\n")<br />
end<br />
<br />
</source></div>Aiaustinhttp://opensimulator.org/wiki/ConfigurationConfiguration2023-01-14T09:40:55Z<p>Aiaustin: /* Running OpenSimulator in Standalone mode */ qualification about need to change PublicPort = 8002 to PublicPort = 9000 curremtly. May change.</p>
<hr />
<div>{{Quicklinks}}<br />
<br /><br />
== OpenSimulator simulator configuration file ==<br />
The region simulator configuration is managed using a file called OpenSim.ini. This file is used regardless of whether the sim is running in standalone or grid mode. This file references some additional configuration information from the config-include/ directory. Information about the various settings is contained in the [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] file itself (or OpenSim.ini.example for reference).<br />
<br />
Please note, that the name [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] can be changed via [[OpenSim.exe Command Line Options|command line arguments]].<br />
<br />
It is also possible to distribute the inifile settings over two files. This is useful if you want to run several OpenSimulator processes where most of your settings are identical except for a few. The master file is read first, then the inifile is read. Settings given in the inifile overrule settings given in the master file. The master file has the same format and the same keywords as the inifile, so the same documentation applies.<br />
<br />
== Database ==<br />
Opensimulator supports the following database engines. Information about setting these up can be found in the OpenSim.ini.example file and the other various example files in bin/config-include. '''If you do not want to use the default SQLite configuration then you will need to setup your database before proceeding further'''. SQLite does not require further configuration. See [[Database Settings]] for the detailed settings.<br />
<br />
* '''SQLite''' (default) - a lightweight database that comes bundled with OpenSimulator and can be used without requiring any extra configuration. It is mostly intended to get you up and running quickly, not for production use. It is significantly slower than MySQL. A few features here (such as attachment persistence) have not yet been fully implemented. <br />
<br />
* '''MySQL''' (fully supported) - This is the recommended database for any use beyond experimentation or small standalone applications. The minimum MySQL version is 5.1.<br />
* '''MariaDB''' (fully supported) - A compatible alternative to MySQL. You need to make sure the selected charset is utf8mb3 (ie 3 bytes). Some instalations default to uft8mb4 (4 bytes) and that will fail<br />
<br />
* '''MSSQL''' (fairly supported) - persistence support for some recent OpenSimulator features may not yet be implemented though the vast majority of them are supported.<br />
<br />
== Standalone vs. Grid ==<br />
We recommend that you first get OpenSimulator running in standalone mode before you attempt to connect it to a grid or run your own grid. OpenSimulator will start up in standalone mode out-of-the-box on the binary distributions.<br />
<br />
An OpenSimulator configuration consists of regions (run by region simulators) and backend data services (such as user, assets and inventory management).<br />
<br />
A system running in '''standalone mode''' runs both the region simulator and all the data services in a single process when you run OpenSim.exe. In this mode you can run as many regions as you like but only on a single machine.<br />
<br />
[[Image:Opensim-standalone.png|frame|center|OpenSimulator running in standalone mode. Both simulator and services run in the same process (OpenSim.exe).]]<br />
<br />
In '''grid mode''', the data services are not part of the region server process. Instead, they are run in a separate executable called Robust.exe. A Robust shell can run all the services or they can be split amongst any number of Robust instances. This allows them to be run on entirely separate machines if necessary. In this mode, the OpenSim.exe acts solely as the region server, serving one or more regions that communicate with the separate data services. At this point you can run multiple OpenSim.exe region simulators on different machines.<br />
<br />
[[Image:Opensim-grid-simple.png|frame|center|OpenSimulator running in grid mode. In this case, all the services are being run within a Robust.exe process. Multiple copies of OpenSim.exe (usually running on different machines) all use the same set of common services.]]<br />
<br />
Running in grid mode is more complicated than running in standalone mode. It requires an understanding of UUID, X,Y location, server handshake passwords, estates and estate owners, and a couple of other settings. These require more care and patience to set up. We strongly recommend that you don't attempt this unless you are extremely patient and very technically proficient.<br />
<br />
= Running OpenSimulator in Standalone mode =<br />
<br />
Binary distributions of OpenSimulator are by default configured to run in standalone mode. ''This is not quite true as at 14-Jan-2023... you need also to edit bin/OpenSim.ini to change PublicPort = 8002 to PublicPort = 9000. So check this.''<br />
<br />
However, if you build OpenSimulator from the source distribution or from the git repository then you will need to:<br />
<br />
# Change into the '''''bin''''' folder<br />
# Copy the file '''''OpenSim.ini.example''''' to '''''[[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]]'''''. This configures the 3D simulator itself.<br />
# Check the '''[Const]''' section PublicPort. It should be set to 9000 by default for a standalone.<br />
# In the '''[Architecture]''' section of '''''[[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]]''''', near the bottom of the file, uncomment the ''Standalone.ini'' line. To uncomment a line of code, remove the semi-colon (;) comment symbol preceding the line.<br />
# Change into the '''''bin/config-include''''' folder<br />
# Copy the file '''''StandaloneCommon.ini.example''''' to '''''StandaloneCommon.ini'''''. This configures the in-process data services used by the standalone configuration.<br />
# Copy the file '''''FlotsamCache.ini.example''''' to '''''FlotsamCache.ini'''''. This configures the cache services used by the standalone configuration.<br />
<br />
Running OpenSimulator is then a matter of launching OpenSim.exe. However, you need to have installed all dependencies before that. See [[Dependencies]] for details. After that, open a command prompt (for Windows users, Start menu > Run > cmd) and navigate to the Opensim /bin directory.<br />
<br />
Under '''Windows''', run:<br />
OpenSim.exe<br />
On '''Linux''' run:<br />
mono OpenSim.exe<br />
<br />
== Running for the first time ==<br />
<br />
If you're running OpenSimulator for the first time, it will ask you several questions at the console that will set up a single region for you. The configuration options you enter will be written to the bin/Regions/Regions.ini file, which you can then edit at a later date if you need to make changes.<br />
<br />
Many of the questions have defaults. Here are some explanations of the questions asked:<br />
<br />
* '''New region name'''<br />
::The name for your region. Don't leave this blank!<br />
* '''Region UUID'''<br />
::The unique ID of your region. In pretty much all cases you will want to accept the randomly generated default in the square brackets. The only time when you wouldn't is if you were trying to set up a configuration to point to pre-existing region data. But in this case you are probably better off editing the Regions.ini file directly anyway<br />
* '''Region Location'''<br />
::This is the location of the region on the grid. In standalone mode you can safely leave these as the default (1000,1000). If you were to set up additional regions later on in Regions.ini then they would need different grid co-ordinates (e.g. 1000,1001). OpenSimulator regions can be placed anywhere on a 65536 by 65536 grid, but [[Hypergrid]] enabled regions may need special consideration for region location. See [[Installing and Running Hypergrid#The 4096 Regions Limit]] for more information.<br />
* '''Internal IP address'''<br />
::In virtually all cases this can be left as 0.0.0.0 (this is a wildcard that allows OpenSimulator to listen for UDP connections on any of the server's network interfaces). If you want to restrict UDP connections to only one network interface then you can specify an explicit IP address. This address is only used internally - the '''External host name''' is the one that is actually passed to the viewer (and hence is the important one).<br />
* '''Internal port'''<br />
::This is the IP port for all incoming client connections. The name is a bit misleading since it will be used externally (by a Second Life viewer, for instance) as well as internally. You can make this any port you want, but it is safe to leave at the default 9000. Each region on your server must have a unique port.<br />
* '''Allow alternate ports'''<br />
::This is currently experimental. Please leave it at the default of False.<br />
* '''External host name'''<br />
::If you leave this at the default 'SYSTEMIP' then this will become the LAN network address of the machine (e.g. 192.168.1.2). This is fine if you are connecting only from within your LAN. If you want to connect to it from a client on the internet, this should be the External IP Address of your router. Fully Qualified Domain Names (FQDNs) can also be used though they will be converted to a numeric IP address before being sent to the viewer.<br />
<br />
OpenSimulator will ask you to assign each region to an estate during the setup process. If an estate needs to be created then it will also ask you to assign an estate manager. In standalone mode, an estate manager can also be created during the setup process.<br />
<br />
Don't forget the account details you use to set up the master avatar (in 0.6.9) or the estate manager (in 0.7 and later). Only this user will initially be able to configure the in-world settings for your region. This is also a user account that you can use to perform your initial login test.<br />
<br />
See [[Configuring Regions]] for more information about the Regions.ini file that these questions generate.<br />
<br />
If you want to create a user other than the estate manager, then in the server console type:<br />
<br />
create user<br />
<br />
This will ask you a series of questions for creating a user (such as first name, last name and password).<br />
<br />
== Network access for the standalone installation ==<br />
In standalone mode, a viewer connecting to your installation needs the following network access to your installation machine.<br />
<br />
1. TCP over the http_listener_port as used in your simulator. This is set in the [Network] section of your [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]].<br />
<br />
<source lang="ini"><br />
[Network]<br />
http_listener_port = 9000<br />
</source><br />
<br />
This will be the default of 9000 if you have not explicitly changed it.<br />
<br />
2. UDP over each region's InternalPort as configured in your region files (such as Regions.ini). For instance, if you have configured<br />
<br />
<source lang="ini"><br />
[test]<br />
RegionUUID = dd5b77f8-bf88-45ac-aace-35bd76426c81<br />
Location = 1000,1000<br />
SizeX = 256<br />
SizeY = 256<br />
SizeZ = 256<br />
InternalAddress = 0.0.0.0<br />
InternalPort = 9000<br />
AllowAlternatePorts = False<br />
ExternalHostName = mygrid.com<br />
<br />
[test2]<br />
RegionUUID = dd5b77f8-bf88-45ac-aace-35bd76426c82<br />
Location = 1000,1001<br />
SizeX = 256<br />
SizeY = 256<br />
SizeZ = 256<br />
InternalAddress = 0.0.0.0<br />
InternalPort = 9001<br />
AllowAlternatePorts = False<br />
ExternalHostName = mygrid.com<br />
</source><br />
<br />
then you will need to open ports 9000 and 9001 to UDP traffic.<br />
<br />
3. The network address of the machine hosting the OpenSimulator installation must be accessible to connecting viewers. In the example above, the installation machine is reachable from the Internet via the domain name "mygrid.com". If the same installation needs to be accessed by viewers on the same network, it must be possible for them to also successfully resolve that domain name (not all routers, especially home routers, have this "loopback capability").<br />
<br />
If the installation only needed to be accessed on the same LAN, then one could you the local IP address of the server (e.g. 192.168.1.2).<br />
<br />
== Connecting to a standalone installation ==<br />
<br />
To connect to your new sim with your user, start up a viewer with the following command line switches:<br />
<br />
'''Client on same machine as OpenSim:'''<br />
-loginuri http://127.0.0.1:9000<br />
<br />
'''Client on same LAN as OpenSim:'''<br />
-loginuri http://lan_ip:9000<br />
<br />
'''Client on different machine or internet:'''<br />
-loginuri http://external_ip:9000<br />
<br />
Then enter the user name and password you set up in the previous step and your new user should login.<br />
<br />
Be aware of [http://osgrid.org/forums/viewtopic.php?f=5&t=400&start=0&st=0&sk=t&sd=a loopback] problems when Running viewer &amp; server(s) on the same machine (LAN) by using the "external" configuration. (<u>'''You might notice endless waiting for region handshake'''</u>.) See also [[Troubleshooting|troubleshoot hints]]. If you're having Connectivity problems, [[Network Settings|be sure to read the Network Configuration Page]]. This is important if you see Region handshake issue.<br />
<br />
[[Image:Exclamation.png|left]]<br />
<br />
== IMPORTANT NOTE, DIVA DISTRO - 4 Apr. 2012 - ==<br />
<br />
'''If you download the latest version of diva-r18611.tar.bz''', it is necessary to first launch the setup program ''configure.exe''<br />
*In Linux or MacOSX : open a terminal and enter "mono /diva-r18611/bin/Configure.exe" (assuming that you have placed the Diva distro in /diva-r18611)<br />
*In Windows, assuming they extracted Diva in My Documents, one would open "Run => cmd" and enter '''cd "%USERPROFILE%\My Documents\diva-r18611\"'', followed by "Configure.exe". <br />
After issuing the command, you can set your sim's domain name, and carefully answer the program's questions, then start the program as instructed in above paragraphs.<br />
<br />
The program will install the optimum configuration for OpenSim, example: '''<nowiki>http://<your_IP>:9000</nowiki>''' and WiFi '''<nowiki>http:<your_IP>:9000/wifi</nowiki>''' <br />
In the standalone version, four regions will be set up. You can optionally add other regions later on, so make sure to use the same first name with the addition of a number <br />
(ex: "region 5", "region 6", "region 7", etc. otherwise you can't enter the region and you'd be placed in the nearest free location.<br />
<br />
If you wish to enter a different region name, make sure that the "distance" between the island created by the Wifi configuration program and the next, <br />
will be at least 40 positions away from the first installed region)<br />
(command console: create region Johnnyland RegionConfigure.ini)<br />
<br />
= Running OpenSimulator in Grid mode =<br />
<br />
Running OpenSimulator in grid mode is considerably more complicated than running a standalone instance. Instead of running everything in the same process, backend data services (asset, inventory, etc.) run in one or more separate processes, often on a different machine. This allows multiple OpenSim.exe simulator instances to use the same asset and inventory data.<br />
<br />
== Step 1: Set up a ROBUST services instance ==<br />
<br />
1. In the bin directory, copy Robust.ini.example to Robust.ini. The example file is configured to run all the services in a single ROBUST instance.<br />
<br />
2. Configure the [[Database Settings]] in Robust.ini to use your MySQL database. Only MySQL is supported for running grid services. <br />
<br />
3. Start up Robust.exe. <br />
<br />
mono Robust.exe (Linux, BSD, Mac OS X)<br />
<br />
or<br />
<br />
Robust.exe (Windows)<br />
<br />
If you don't see any errors (in red) on the console then you can move on to the next step.<br />
<br />
4. Every region must belong to an estate, and every estate must have an owner which is a valid user account in OpenSim's user account service. Create a user on the ROBUST command console with the following command.<br />
<br />
create user<br />
<br />
This will ask you for the user's name, password and an optional e-mail. Remember this name since you will need it when you start up the simulator for the first time.<br />
<br />
== Step 2: Configure an OpenSim.exe to use the ROBUST services ==<br />
<br />
In grid mode, as in standalone mode, you need to configure [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] which controls the 3D simulator itself.<br />
<br />
However, instead of using and configuring the file config-include/StandaloneCommon.ini, a simulator connecting to a grid needs to use and configure the config-include/GridCommon.ini file, in order to connect to the ROBUST hosted remote data services rather than in-process local ones.<br />
<br />
The steps for both these operations are as follows.<br />
<br />
1. Copy bin/OpenSim.ini.example to [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]]<br />
<br />
2. Find the [Architecture] section at the very bottom of OpenSim.ini. Make sure that one of the following lines is uncommented:<br />
<br />
Include-Architecture = "config-include/Grid.ini" (in OpenSimulator 0.7.1 and later)<br />
<br />
or<br />
<br />
Include-Grid = "config-include/Grid.ini" (in OpenSimulator 0.7.0.2 and earlier)<br />
<br />
The others should remain commented.<br />
<br />
3. Go to bin/config-include and copy GridCommon.ini.example to GridCommon.ini.<br />
<br />
4. Open GridCommon.ini in a text editor. You will see lots of URL entries, each of which have dummy defaults of http://myassetserver.com:8003, http://myinventoryserver.com:8003, etc. You will need to change each of these to point towards the address of your ROBUST instance. For instance, if you're running ROBUST on a machine with a local IP address of 192.168.1.2, you will need to change AssetServerURI to the setting<br />
<br />
AssetServerURI = "http://192.168.1.2:8003"<br />
<br />
5. Run OpenSim.exe. If you're running OpenSim.exe for the first time you will get the same questions about setting up the region that occur on a first-run in standalone mode. Please see the standalone section for instructions on how to answer these, or read more information about the Regions.ini file on the [[Configuring Regions]] page.<br />
<br />
If everything is set up correctly, when starting up OpenSim.exe you shouldn't see any errors. You should also see the ROBUST console display log lines saying that the region has registered with the grid service. For example,<br />
<br />
21:43:45 - [GRID SERVICE]: Region t1 (176cc95e-f693-4b02-8e08-af86e2372faa) registered successfully at 256000-256000<br />
21:43:47 - [GRID SERVICE]: region t1 has 0 neighbours<br />
<br />
== Network access for a grid installation ==<br />
In standalone mode, a viewer connecting to your installation needs to access<br />
<br />
# The login service over TCP and other configured public services (e.g. grid info, map).<br />
# The http_server_port of each configured simulator over TCP.<br />
# The InternalPort and ExternalHostName of each configured region.<br />
<br />
The last two requirements are the same as for standalone installations, except that they apply to each server that hosts a simulator. Please see the standalone section above for more details.<br />
<br />
The login service is a little different. Whereas in standalone this uses the same http_server_port as the simulator itself, in grid mode it's running in a separate ROBUST service.<br />
<br />
The default port for the login service is 8002. You can see this used in the [ServiceList] section of Robust.ini.example.<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector"<br />
InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector"<br />
GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector"<br />
GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector"<br />
AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector"<br />
OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector"<br />
AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector"<br />
LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector"<br />
PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector"<br />
UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector"<br />
GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector"<br />
FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector"<br />
MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector"<br />
MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"<br />
</source><br />
<br />
Here all the public services (those where the viewer connects directly to the service) are served on port 8002, with internal services on 8003. So you need to make sure that the viewer can access port 8002 over TCP but you do not want to expose port 8003. Only simulators need to be able to connect to port 8003.<br />
<br />
== Connecting to a grid installation ==<br />
<br />
Your client startup line will look something like<br />
<br />
-loginuri http://mygrid.com:8002<br />
<br />
The loginuri needs to be the address to the login service. In standalone mode, this was the same address as the region simulator and the port was 9000 by default. However, in grid mode it will be the address to login service hosted on the ROBUST instance. In this case, the address will be 192.168.1.2. The port number of 8002 is the traditional one for the grid login service and is the default in Robust.ini.example.<br />
<br />
If the login is successful, you will see log lines on the ROBUST console (for the login itself) and then log lines on the region simulator console (as the login process tells the simulator to expect the avatar, tells the viewer the address of the region simulator and then when the viewer starts talking to the simulator directly).<br />
<br />
= Running multiple ROBUST service instances =<br />
<b>WARNING:<br><br />
This is a tricky issue and contrary to past allegations, opensim is not well prepared for this kind of microservices concepts.<br><br />
You can split some services by different machines, if they have no interdependencies. For example current XBakes can run anywhere<br><br />
Running several instances of same service is a invite to fail.<br><br />
Some services do assume they are the only instance, like current PresenceService, GriduserService, etc, and<br />
even do own caching to reduce load on the database engine, and that cache cannot be syncronized across instances<br><br />
Most will only service http in paralell, placing the same, and most of the load, on the same database engine. So unless that database service is a distributed system, doing proper local caching and synchronization with a master database, the gains of having multiple instances is not that large, if any if if not a total fail<br><br />
<br />
Another important thing to note is that currently several services need to read data from the configuration of other services. For example Gridservice needs to read data from its section, GridService, and sections LoginService, Hypergrid and GridInfoService. So the ini file for the robust instance running it needs to have those sections, and of course updated. <br />
</b><br><br />
<br />
<br />
If you are operating a grid, then you can run different services (e.g. asset, inventory) in different ROBUST instances, in order to spread the load. To do this, you will need to edit the ServiceConnectors parameter in the [Startup] section of Robust.ini (or [[Configuration/files/Robust/Robust.HG.ini|Robust.HG.ini]] if you're running Hypergrid). The default ServiceConnectors parameter looks something like this<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector"<br />
InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector"<br />
GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector"<br />
GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector"<br />
AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector"<br />
OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector"<br />
AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector"<br />
LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector"<br />
PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector"<br />
UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector"<br />
GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector"<br />
FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector"<br />
MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector"<br />
MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"<br />
</source><br />
<br />
Each entry has the form <br />
<br />
<port-number>/<dll>:<connector-class-name><br />
<br />
For instance, the first entry above<br />
<br />
8003/OpenSim.Server.Handlers.dll:AssetServiceConnector<br />
<br />
says to start an AssetServiceConnector (and hence an asset service) from the OpenSim.Server.Handlers.dll and to server that from port 8003.<br />
<br />
By default, Robust.exe loads a configuration file with the same name but with .ini appended instead of .exe. So Robust.exe will look for an inifile called Robust.ini. You can change this by giving the parameter on the commandline. For instance, to start Robust with HG parameters, one would use<br />
<br />
Robust.exe -inifile=Robust.HG.ini<br />
<br />
So if you wanted to run every connector apart from assets in one instance of ROBUST and the asset connector in another instance, you would have two ini files. One could remain Robust.ini and have<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector"<br />
GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector"<br />
GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector"<br />
AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector"<br />
OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector"<br />
AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector"<br />
LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector"<br />
PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector"<br />
UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector"<br />
GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector"<br />
FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector"<br />
MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector"<br />
MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"<br />
</source><br />
<br />
The other could be called Robust.Assets.ini and have<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
AssetServiceConnector = "8004/OpenSim.Server.Handlers.dll:AssetServiceConnector"<br />
</source><br />
<br />
Note that this is using port 8004 instead of port 8003. This is necessary since only one executable can use each port at a time. You will need to make sure your simulator configuration files use port 8004 for the asset service as well.<br />
<br />
You will also need to change the default network port to 8004 for this second copy of Robust.exe<br />
<br />
<pre><br />
[Network]<br />
port = 8004<br />
</pre><br />
<br />
Once you've created the two ROBUST configuration files (Robust.ini containing all services apart from asset and Robust.Assets.ini containing only the asset service), then you could start the first Robust.exe as usual.<br />
<br />
Robust.exe<br />
<br />
This will load Robust.ini, as we haven't specified a Robust.ini. Also, the logfile it will use will be Robust.log as we haven't manually specified one.<br />
<br />
The second ROBUST instance we would start with<br />
<br />
Robust.exe -inifile=Robust.Assets.ini -logfile=Robust.Assets.log<br />
<br />
The -inifile switch tells the second Robust instance to load it's configuration from Robust.Assets.ini rather than Robust.ini. The -logfile switch tells Robust.exe to use Robust.Assets.log as its logfile rather than the default Robust.log. If you don't specify this switch then you may see errors on the console about a locked log file.<br />
<br />
At this point you should have two running Robust.exe instances.<br />
<br />
If you put the ROBUST instances on different machines then don't forget to change the relevant service URIs in each simulator to match.<br />
<br />
Since OpenSimulator services are stateless (e.g. every request is unconnected with other requests as long as they affect the same persistent data where necessary), you can also load balance by starting more than one ROBUST instance with a copy of the same service (e.g. multiple asset services using the same database). Requests would be round-robined between the service copies using an HTTP reverse proxy implementation (e.g. nginx). See [[Performance#Services]] for more details.<br />
<br />
== Notes ==<br />
[http://opensimulator.org/pipermail/opensim-users/2012-October/011099.html Useful discussion about separating Robust instances]<br />
<br />
= Attaching your sim to someone else's grid =<br />
* Make sure you understand, agree and will enforce that grid Terms of Service<br />
* Follow the instructions of that grid owner!<br />
<br />
To set up the region server (i.e., <tt>OpenSim.exe</tt>) to connect to an external grid, follow the [[Configuration#Step 2: Configure an OpenSim.exe to use the ROBUST services]] instructions above.<br />
<br />
The grid will have already provided with the required services. In step 2 you will need to use the provided URLs for their services.<br />
<br />
In your bin/Regions.ini file (or other region config file) you will also need to set the grid co-ordinates to your regions provided from the grid operator. See [[Configuring Regions]] for more information.<br />
<br />
= Running an OpenSimulator standalone or grid installation with Hypergrid enabled =<br />
[[Hypergrid]] is an emerging architecture supported by OpenSimulator that allows a user with an account on one standalone or grid to visit other Hypergrid-enabled standalones or grids, and for users from those grids to visit the home grid. This does not require the two installations to share a central set of data services (assets, inventory, etc.). Please see [[Installing and Running Hypergrid]] for more details.<br />
<br />
= Further notes =<br />
<br />
== Troubleshooting ==<br />
<br />
See [[Troubleshooting]] <br />
<br />
== Running OpenSimulator in 32 bit in Windows 64bit==<br />
<br />
Windows 64Bit can run several instances of OpenSim in 32Bit mode, allowing each to use up to about 2GB of total physical ram.<br />
<br />
This is almost the total memory available on 32Bit versions of Windows, for all the programs (around 3GB)<br />
<br />
In 32Bit mode, the simulator uses less memory and may be a bit faster, so unless you have very large regions, you can ran it in 32Bit mode, just run:<br />
<br />
OpenSim32.exe instead of OpenSim.exe<br />
<br />
You can also run Robust in 32Bit mode:<br />
<br />
Robust32.exe in place of Robust.exe<br />
<br />
Linux 64Bit only runs the simulator in 64Bit mode<br />
<br />
== Increasing the stack reserve level when using OpenDynamicsEngine (old ODE and ubODE) on Linux or other Unix variants ==<br />
<br />
Increase your stack reserve level with the following command;<br />
<tt>ulimit -s 262144</tt> Or, run the opensim.sh to start up OpenSimulator.<br />
<br />
== Firewalls ==<br />
Some operation systems or distributions run their own firewall by default. If you can't access to OpenSimulator from remote client, you'll need to check their settings. See [[Firewall Settings]] for details.<br />
<br />
== Legacy Configuration Information ==<br />
These are some pages containing some legacy configuration information of unknown accuracy.<br />
<br />
[[OpenSim 0.6.6 legacy configuration information]]<br />
<br />
<br />
== Additional Optional Configuration Tasks ==<br />
<br />
=== Further configure OpenSimulator ===<br />
If you've looked through OpenSim.ini.example or any other of the config files, you'll see that there's a very large number of configurable parameters spread across multiple files. See [[Configuring Simulator Parameters]] for more details about the configuration infrastructure and how settings in identically named sections (e.g. [XEngine]) are merged by OpenSimulator on loading.<br />
<br />
=== Set up a second region to run on the same simulator ===<br />
See [[Configuring Regions]].<br />
<br />
=== Run Multiple Standalone Instances of OpenSimulator on the Same Server ===<br />
For each subsequent instance of OpenSim, change the 'http_listener_port' in [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] to the value excluding 9000, and 'InternalPort' in Regions.ini to the value excluding 9000. Also, make sure your regions are using different ports, as explained in [[Configuring Regions]].<br />
<br />
=== Load region content ===<br />
You can load content onto regions by using the [[OpenSim Archives|load oar command]]. To load individual OAR files into each region, use the 'change region [regionname]' command and then 'load oar [oar-location]'.<br />
<br />
=== OpenSim.exe command line options ===<br />
OpenSim.exe has command line options which allow you to perform actions such as reading configuration files from a different directory. See [[OpenSim.exe Command Line Options]] for more details.<br />
<br />
=== Script engine ===<br />
OpenSimulator supports multiple script engines. See [[ScriptEngines]] for details. If you don't know what this means then the default script engine will be fine. In fact, recent versions of OpenSimulator only ship with one script engine, the XEngine.<br />
<br />
=== Permissions Configuration ===<br />
OpenSimulator has a quite elaborate set of permissions. See [[Permissions (Server)]] for details. By default, permissions are active on region simulators.<br />
<br />
=== Logging ===<br />
By default, OpenSimulator logs information to a file called OpenSim.log in the bin directory. See [[Logging]] for details on how to further configure this if required.<br />
<br />
=== FSAsset Service ===<br />
By default the OpenSimulator asset service will store assets in the robust database. If you expect your asset data to grow larger than 50Gb you should consider changing to the [[FSAssets Service]]. (FSAssets is a new service and is currently only available in the latest development branch)<br />
<br />
=== Set up other optional modules ===<br />
* [[IRCBridgeModule]]<br />
* [[Freeswitch Module]]<br />
* [[Offline Messaging]]<br />
* [[Enabling Groups]]<br />
* See also [[User_Documentation#Setup]]<br />
<br />
=== Configuration of Web Server and Pages ===<br />
OpenSimulator contains a web server that can serve up a variety of pages. Some which come from external files and some are generated internally.<br />
* [[External Files]]<br />
* [[Internally Generated]]<br />
<br />
= Where to go from here =<br />
<br />
* [[NAT Loopback Routers]] Router and Nat Loopback Information<br />
<br />
* [[Upgrading]] from an old OpenSimulator version to a newer one.<br />
<br />
* [[Configuring_Simulator_Parameters]] cascading configuration structure, environment variables<br />
<br />
* [[Server Commands]] for creating users and controlling the system.<br />
<br />
= References =<br />
<br />
* [http://dist.opensimulator.org/wiki/opensim-standalone.odg OpenOffice draw file for OpenSimulator standalone diagram]<br />
* [http://dist.opensimulator.org/wiki/opensim-grid-simple.odg OpenOffice draw file for OpenSimulator grid diagram]<br />
<br />
[[Category:Configuration]]</div>Aiaustinhttp://opensimulator.org/wiki/ConfigurationConfiguration2023-01-14T09:34:42Z<p>Aiaustin: /* Running OpenSimulator in Standalone mode */ Format only</p>
<hr />
<div>{{Quicklinks}}<br />
<br /><br />
== OpenSimulator simulator configuration file ==<br />
The region simulator configuration is managed using a file called OpenSim.ini. This file is used regardless of whether the sim is running in standalone or grid mode. This file references some additional configuration information from the config-include/ directory. Information about the various settings is contained in the [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] file itself (or OpenSim.ini.example for reference).<br />
<br />
Please note, that the name [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] can be changed via [[OpenSim.exe Command Line Options|command line arguments]].<br />
<br />
It is also possible to distribute the inifile settings over two files. This is useful if you want to run several OpenSimulator processes where most of your settings are identical except for a few. The master file is read first, then the inifile is read. Settings given in the inifile overrule settings given in the master file. The master file has the same format and the same keywords as the inifile, so the same documentation applies.<br />
<br />
== Database ==<br />
Opensimulator supports the following database engines. Information about setting these up can be found in the OpenSim.ini.example file and the other various example files in bin/config-include. '''If you do not want to use the default SQLite configuration then you will need to setup your database before proceeding further'''. SQLite does not require further configuration. See [[Database Settings]] for the detailed settings.<br />
<br />
* '''SQLite''' (default) - a lightweight database that comes bundled with OpenSimulator and can be used without requiring any extra configuration. It is mostly intended to get you up and running quickly, not for production use. It is significantly slower than MySQL. A few features here (such as attachment persistence) have not yet been fully implemented. <br />
<br />
* '''MySQL''' (fully supported) - This is the recommended database for any use beyond experimentation or small standalone applications. The minimum MySQL version is 5.1.<br />
* '''MariaDB''' (fully supported) - A compatible alternative to MySQL. You need to make sure the selected charset is utf8mb3 (ie 3 bytes). Some instalations default to uft8mb4 (4 bytes) and that will fail<br />
<br />
* '''MSSQL''' (fairly supported) - persistence support for some recent OpenSimulator features may not yet be implemented though the vast majority of them are supported.<br />
<br />
== Standalone vs. Grid ==<br />
We recommend that you first get OpenSimulator running in standalone mode before you attempt to connect it to a grid or run your own grid. OpenSimulator will start up in standalone mode out-of-the-box on the binary distributions.<br />
<br />
An OpenSimulator configuration consists of regions (run by region simulators) and backend data services (such as user, assets and inventory management).<br />
<br />
A system running in '''standalone mode''' runs both the region simulator and all the data services in a single process when you run OpenSim.exe. In this mode you can run as many regions as you like but only on a single machine.<br />
<br />
[[Image:Opensim-standalone.png|frame|center|OpenSimulator running in standalone mode. Both simulator and services run in the same process (OpenSim.exe).]]<br />
<br />
In '''grid mode''', the data services are not part of the region server process. Instead, they are run in a separate executable called Robust.exe. A Robust shell can run all the services or they can be split amongst any number of Robust instances. This allows them to be run on entirely separate machines if necessary. In this mode, the OpenSim.exe acts solely as the region server, serving one or more regions that communicate with the separate data services. At this point you can run multiple OpenSim.exe region simulators on different machines.<br />
<br />
[[Image:Opensim-grid-simple.png|frame|center|OpenSimulator running in grid mode. In this case, all the services are being run within a Robust.exe process. Multiple copies of OpenSim.exe (usually running on different machines) all use the same set of common services.]]<br />
<br />
Running in grid mode is more complicated than running in standalone mode. It requires an understanding of UUID, X,Y location, server handshake passwords, estates and estate owners, and a couple of other settings. These require more care and patience to set up. We strongly recommend that you don't attempt this unless you are extremely patient and very technically proficient.<br />
<br />
= Running OpenSimulator in Standalone mode =<br />
<br />
Binary distributions of OpenSimulator are by default configured to run in standalone mode.<br />
<br />
However, if you build OpenSimulator from the source distribution or from the git repository then you will need to:<br />
<br />
# Change into the '''''bin''''' folder<br />
# Copy the file '''''OpenSim.ini.example''''' to '''''[[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]]'''''. This configures the 3D simulator itself.<br />
# Check the '''[Const]''' section PublicPort. It should be set to 9000 by default for a standalone.<br />
# In the '''[Architecture]''' section of '''''[[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]]''''', near the bottom of the file, uncomment the ''Standalone.ini'' line. To uncomment a line of code, remove the semi-colon (;) comment symbol preceding the line.<br />
# Change into the '''''bin/config-include''''' folder<br />
# Copy the file '''''StandaloneCommon.ini.example''''' to '''''StandaloneCommon.ini'''''. This configures the in-process data services used by the standalone configuration.<br />
# Copy the file '''''FlotsamCache.ini.example''''' to '''''FlotsamCache.ini'''''. This configures the cache services used by the standalone configuration.<br />
<br />
Running OpenSimulator is then a matter of launching OpenSim.exe. However, you need to have installed all dependencies before that. See [[Dependencies]] for details. After that, open a command prompt (for Windows users, Start menu > Run > cmd) and navigate to the Opensim /bin directory.<br />
<br />
Under '''Windows''', run:<br />
OpenSim.exe<br />
On '''Linux''' run:<br />
mono OpenSim.exe<br />
<br />
== Running for the first time ==<br />
<br />
If you're running OpenSimulator for the first time, it will ask you several questions at the console that will set up a single region for you. The configuration options you enter will be written to the bin/Regions/Regions.ini file, which you can then edit at a later date if you need to make changes.<br />
<br />
Many of the questions have defaults. Here are some explanations of the questions asked:<br />
<br />
* '''New region name'''<br />
::The name for your region. Don't leave this blank!<br />
* '''Region UUID'''<br />
::The unique ID of your region. In pretty much all cases you will want to accept the randomly generated default in the square brackets. The only time when you wouldn't is if you were trying to set up a configuration to point to pre-existing region data. But in this case you are probably better off editing the Regions.ini file directly anyway<br />
* '''Region Location'''<br />
::This is the location of the region on the grid. In standalone mode you can safely leave these as the default (1000,1000). If you were to set up additional regions later on in Regions.ini then they would need different grid co-ordinates (e.g. 1000,1001). OpenSimulator regions can be placed anywhere on a 65536 by 65536 grid, but [[Hypergrid]] enabled regions may need special consideration for region location. See [[Installing and Running Hypergrid#The 4096 Regions Limit]] for more information.<br />
* '''Internal IP address'''<br />
::In virtually all cases this can be left as 0.0.0.0 (this is a wildcard that allows OpenSimulator to listen for UDP connections on any of the server's network interfaces). If you want to restrict UDP connections to only one network interface then you can specify an explicit IP address. This address is only used internally - the '''External host name''' is the one that is actually passed to the viewer (and hence is the important one).<br />
* '''Internal port'''<br />
::This is the IP port for all incoming client connections. The name is a bit misleading since it will be used externally (by a Second Life viewer, for instance) as well as internally. You can make this any port you want, but it is safe to leave at the default 9000. Each region on your server must have a unique port.<br />
* '''Allow alternate ports'''<br />
::This is currently experimental. Please leave it at the default of False.<br />
* '''External host name'''<br />
::If you leave this at the default 'SYSTEMIP' then this will become the LAN network address of the machine (e.g. 192.168.1.2). This is fine if you are connecting only from within your LAN. If you want to connect to it from a client on the internet, this should be the External IP Address of your router. Fully Qualified Domain Names (FQDNs) can also be used though they will be converted to a numeric IP address before being sent to the viewer.<br />
<br />
OpenSimulator will ask you to assign each region to an estate during the setup process. If an estate needs to be created then it will also ask you to assign an estate manager. In standalone mode, an estate manager can also be created during the setup process.<br />
<br />
Don't forget the account details you use to set up the master avatar (in 0.6.9) or the estate manager (in 0.7 and later). Only this user will initially be able to configure the in-world settings for your region. This is also a user account that you can use to perform your initial login test.<br />
<br />
See [[Configuring Regions]] for more information about the Regions.ini file that these questions generate.<br />
<br />
If you want to create a user other than the estate manager, then in the server console type:<br />
<br />
create user<br />
<br />
This will ask you a series of questions for creating a user (such as first name, last name and password).<br />
<br />
== Network access for the standalone installation ==<br />
In standalone mode, a viewer connecting to your installation needs the following network access to your installation machine.<br />
<br />
1. TCP over the http_listener_port as used in your simulator. This is set in the [Network] section of your [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]].<br />
<br />
<source lang="ini"><br />
[Network]<br />
http_listener_port = 9000<br />
</source><br />
<br />
This will be the default of 9000 if you have not explicitly changed it.<br />
<br />
2. UDP over each region's InternalPort as configured in your region files (such as Regions.ini). For instance, if you have configured<br />
<br />
<source lang="ini"><br />
[test]<br />
RegionUUID = dd5b77f8-bf88-45ac-aace-35bd76426c81<br />
Location = 1000,1000<br />
SizeX = 256<br />
SizeY = 256<br />
SizeZ = 256<br />
InternalAddress = 0.0.0.0<br />
InternalPort = 9000<br />
AllowAlternatePorts = False<br />
ExternalHostName = mygrid.com<br />
<br />
[test2]<br />
RegionUUID = dd5b77f8-bf88-45ac-aace-35bd76426c82<br />
Location = 1000,1001<br />
SizeX = 256<br />
SizeY = 256<br />
SizeZ = 256<br />
InternalAddress = 0.0.0.0<br />
InternalPort = 9001<br />
AllowAlternatePorts = False<br />
ExternalHostName = mygrid.com<br />
</source><br />
<br />
then you will need to open ports 9000 and 9001 to UDP traffic.<br />
<br />
3. The network address of the machine hosting the OpenSimulator installation must be accessible to connecting viewers. In the example above, the installation machine is reachable from the Internet via the domain name "mygrid.com". If the same installation needs to be accessed by viewers on the same network, it must be possible for them to also successfully resolve that domain name (not all routers, especially home routers, have this "loopback capability").<br />
<br />
If the installation only needed to be accessed on the same LAN, then one could you the local IP address of the server (e.g. 192.168.1.2).<br />
<br />
== Connecting to a standalone installation ==<br />
<br />
To connect to your new sim with your user, start up a viewer with the following command line switches:<br />
<br />
'''Client on same machine as OpenSim:'''<br />
-loginuri http://127.0.0.1:9000<br />
<br />
'''Client on same LAN as OpenSim:'''<br />
-loginuri http://lan_ip:9000<br />
<br />
'''Client on different machine or internet:'''<br />
-loginuri http://external_ip:9000<br />
<br />
Then enter the user name and password you set up in the previous step and your new user should login.<br />
<br />
Be aware of [http://osgrid.org/forums/viewtopic.php?f=5&t=400&start=0&st=0&sk=t&sd=a loopback] problems when Running viewer &amp; server(s) on the same machine (LAN) by using the "external" configuration. (<u>'''You might notice endless waiting for region handshake'''</u>.) See also [[Troubleshooting|troubleshoot hints]]. If you're having Connectivity problems, [[Network Settings|be sure to read the Network Configuration Page]]. This is important if you see Region handshake issue.<br />
<br />
[[Image:Exclamation.png|left]]<br />
<br />
== IMPORTANT NOTE, DIVA DISTRO - 4 Apr. 2012 - ==<br />
<br />
'''If you download the latest version of diva-r18611.tar.bz''', it is necessary to first launch the setup program ''configure.exe''<br />
*In Linux or MacOSX : open a terminal and enter "mono /diva-r18611/bin/Configure.exe" (assuming that you have placed the Diva distro in /diva-r18611)<br />
*In Windows, assuming they extracted Diva in My Documents, one would open "Run => cmd" and enter '''cd "%USERPROFILE%\My Documents\diva-r18611\"'', followed by "Configure.exe". <br />
After issuing the command, you can set your sim's domain name, and carefully answer the program's questions, then start the program as instructed in above paragraphs.<br />
<br />
The program will install the optimum configuration for OpenSim, example: '''<nowiki>http://<your_IP>:9000</nowiki>''' and WiFi '''<nowiki>http:<your_IP>:9000/wifi</nowiki>''' <br />
In the standalone version, four regions will be set up. You can optionally add other regions later on, so make sure to use the same first name with the addition of a number <br />
(ex: "region 5", "region 6", "region 7", etc. otherwise you can't enter the region and you'd be placed in the nearest free location.<br />
<br />
If you wish to enter a different region name, make sure that the "distance" between the island created by the Wifi configuration program and the next, <br />
will be at least 40 positions away from the first installed region)<br />
(command console: create region Johnnyland RegionConfigure.ini)<br />
<br />
= Running OpenSimulator in Grid mode =<br />
<br />
Running OpenSimulator in grid mode is considerably more complicated than running a standalone instance. Instead of running everything in the same process, backend data services (asset, inventory, etc.) run in one or more separate processes, often on a different machine. This allows multiple OpenSim.exe simulator instances to use the same asset and inventory data.<br />
<br />
== Step 1: Set up a ROBUST services instance ==<br />
<br />
1. In the bin directory, copy Robust.ini.example to Robust.ini. The example file is configured to run all the services in a single ROBUST instance.<br />
<br />
2. Configure the [[Database Settings]] in Robust.ini to use your MySQL database. Only MySQL is supported for running grid services. <br />
<br />
3. Start up Robust.exe. <br />
<br />
mono Robust.exe (Linux, BSD, Mac OS X)<br />
<br />
or<br />
<br />
Robust.exe (Windows)<br />
<br />
If you don't see any errors (in red) on the console then you can move on to the next step.<br />
<br />
4. Every region must belong to an estate, and every estate must have an owner which is a valid user account in OpenSim's user account service. Create a user on the ROBUST command console with the following command.<br />
<br />
create user<br />
<br />
This will ask you for the user's name, password and an optional e-mail. Remember this name since you will need it when you start up the simulator for the first time.<br />
<br />
== Step 2: Configure an OpenSim.exe to use the ROBUST services ==<br />
<br />
In grid mode, as in standalone mode, you need to configure [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] which controls the 3D simulator itself.<br />
<br />
However, instead of using and configuring the file config-include/StandaloneCommon.ini, a simulator connecting to a grid needs to use and configure the config-include/GridCommon.ini file, in order to connect to the ROBUST hosted remote data services rather than in-process local ones.<br />
<br />
The steps for both these operations are as follows.<br />
<br />
1. Copy bin/OpenSim.ini.example to [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]]<br />
<br />
2. Find the [Architecture] section at the very bottom of OpenSim.ini. Make sure that one of the following lines is uncommented:<br />
<br />
Include-Architecture = "config-include/Grid.ini" (in OpenSimulator 0.7.1 and later)<br />
<br />
or<br />
<br />
Include-Grid = "config-include/Grid.ini" (in OpenSimulator 0.7.0.2 and earlier)<br />
<br />
The others should remain commented.<br />
<br />
3. Go to bin/config-include and copy GridCommon.ini.example to GridCommon.ini.<br />
<br />
4. Open GridCommon.ini in a text editor. You will see lots of URL entries, each of which have dummy defaults of http://myassetserver.com:8003, http://myinventoryserver.com:8003, etc. You will need to change each of these to point towards the address of your ROBUST instance. For instance, if you're running ROBUST on a machine with a local IP address of 192.168.1.2, you will need to change AssetServerURI to the setting<br />
<br />
AssetServerURI = "http://192.168.1.2:8003"<br />
<br />
5. Run OpenSim.exe. If you're running OpenSim.exe for the first time you will get the same questions about setting up the region that occur on a first-run in standalone mode. Please see the standalone section for instructions on how to answer these, or read more information about the Regions.ini file on the [[Configuring Regions]] page.<br />
<br />
If everything is set up correctly, when starting up OpenSim.exe you shouldn't see any errors. You should also see the ROBUST console display log lines saying that the region has registered with the grid service. For example,<br />
<br />
21:43:45 - [GRID SERVICE]: Region t1 (176cc95e-f693-4b02-8e08-af86e2372faa) registered successfully at 256000-256000<br />
21:43:47 - [GRID SERVICE]: region t1 has 0 neighbours<br />
<br />
== Network access for a grid installation ==<br />
In standalone mode, a viewer connecting to your installation needs to access<br />
<br />
# The login service over TCP and other configured public services (e.g. grid info, map).<br />
# The http_server_port of each configured simulator over TCP.<br />
# The InternalPort and ExternalHostName of each configured region.<br />
<br />
The last two requirements are the same as for standalone installations, except that they apply to each server that hosts a simulator. Please see the standalone section above for more details.<br />
<br />
The login service is a little different. Whereas in standalone this uses the same http_server_port as the simulator itself, in grid mode it's running in a separate ROBUST service.<br />
<br />
The default port for the login service is 8002. You can see this used in the [ServiceList] section of Robust.ini.example.<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector"<br />
InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector"<br />
GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector"<br />
GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector"<br />
AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector"<br />
OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector"<br />
AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector"<br />
LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector"<br />
PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector"<br />
UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector"<br />
GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector"<br />
FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector"<br />
MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector"<br />
MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"<br />
</source><br />
<br />
Here all the public services (those where the viewer connects directly to the service) are served on port 8002, with internal services on 8003. So you need to make sure that the viewer can access port 8002 over TCP but you do not want to expose port 8003. Only simulators need to be able to connect to port 8003.<br />
<br />
== Connecting to a grid installation ==<br />
<br />
Your client startup line will look something like<br />
<br />
-loginuri http://mygrid.com:8002<br />
<br />
The loginuri needs to be the address to the login service. In standalone mode, this was the same address as the region simulator and the port was 9000 by default. However, in grid mode it will be the address to login service hosted on the ROBUST instance. In this case, the address will be 192.168.1.2. The port number of 8002 is the traditional one for the grid login service and is the default in Robust.ini.example.<br />
<br />
If the login is successful, you will see log lines on the ROBUST console (for the login itself) and then log lines on the region simulator console (as the login process tells the simulator to expect the avatar, tells the viewer the address of the region simulator and then when the viewer starts talking to the simulator directly).<br />
<br />
= Running multiple ROBUST service instances =<br />
<b>WARNING:<br><br />
This is a tricky issue and contrary to past allegations, opensim is not well prepared for this kind of microservices concepts.<br><br />
You can split some services by different machines, if they have no interdependencies. For example current XBakes can run anywhere<br><br />
Running several instances of same service is a invite to fail.<br><br />
Some services do assume they are the only instance, like current PresenceService, GriduserService, etc, and<br />
even do own caching to reduce load on the database engine, and that cache cannot be syncronized across instances<br><br />
Most will only service http in paralell, placing the same, and most of the load, on the same database engine. So unless that database service is a distributed system, doing proper local caching and synchronization with a master database, the gains of having multiple instances is not that large, if any if if not a total fail<br><br />
<br />
Another important thing to note is that currently several services need to read data from the configuration of other services. For example Gridservice needs to read data from its section, GridService, and sections LoginService, Hypergrid and GridInfoService. So the ini file for the robust instance running it needs to have those sections, and of course updated. <br />
</b><br><br />
<br />
<br />
If you are operating a grid, then you can run different services (e.g. asset, inventory) in different ROBUST instances, in order to spread the load. To do this, you will need to edit the ServiceConnectors parameter in the [Startup] section of Robust.ini (or [[Configuration/files/Robust/Robust.HG.ini|Robust.HG.ini]] if you're running Hypergrid). The default ServiceConnectors parameter looks something like this<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector"<br />
InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector"<br />
GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector"<br />
GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector"<br />
AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector"<br />
OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector"<br />
AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector"<br />
LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector"<br />
PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector"<br />
UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector"<br />
GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector"<br />
FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector"<br />
MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector"<br />
MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"<br />
</source><br />
<br />
Each entry has the form <br />
<br />
<port-number>/<dll>:<connector-class-name><br />
<br />
For instance, the first entry above<br />
<br />
8003/OpenSim.Server.Handlers.dll:AssetServiceConnector<br />
<br />
says to start an AssetServiceConnector (and hence an asset service) from the OpenSim.Server.Handlers.dll and to server that from port 8003.<br />
<br />
By default, Robust.exe loads a configuration file with the same name but with .ini appended instead of .exe. So Robust.exe will look for an inifile called Robust.ini. You can change this by giving the parameter on the commandline. For instance, to start Robust with HG parameters, one would use<br />
<br />
Robust.exe -inifile=Robust.HG.ini<br />
<br />
So if you wanted to run every connector apart from assets in one instance of ROBUST and the asset connector in another instance, you would have two ini files. One could remain Robust.ini and have<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector"<br />
GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector"<br />
GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector"<br />
AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector"<br />
OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector"<br />
AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector"<br />
LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector"<br />
PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector"<br />
UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector"<br />
GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector"<br />
FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector"<br />
MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector"<br />
MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"<br />
</source><br />
<br />
The other could be called Robust.Assets.ini and have<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
AssetServiceConnector = "8004/OpenSim.Server.Handlers.dll:AssetServiceConnector"<br />
</source><br />
<br />
Note that this is using port 8004 instead of port 8003. This is necessary since only one executable can use each port at a time. You will need to make sure your simulator configuration files use port 8004 for the asset service as well.<br />
<br />
You will also need to change the default network port to 8004 for this second copy of Robust.exe<br />
<br />
<pre><br />
[Network]<br />
port = 8004<br />
</pre><br />
<br />
Once you've created the two ROBUST configuration files (Robust.ini containing all services apart from asset and Robust.Assets.ini containing only the asset service), then you could start the first Robust.exe as usual.<br />
<br />
Robust.exe<br />
<br />
This will load Robust.ini, as we haven't specified a Robust.ini. Also, the logfile it will use will be Robust.log as we haven't manually specified one.<br />
<br />
The second ROBUST instance we would start with<br />
<br />
Robust.exe -inifile=Robust.Assets.ini -logfile=Robust.Assets.log<br />
<br />
The -inifile switch tells the second Robust instance to load it's configuration from Robust.Assets.ini rather than Robust.ini. The -logfile switch tells Robust.exe to use Robust.Assets.log as its logfile rather than the default Robust.log. If you don't specify this switch then you may see errors on the console about a locked log file.<br />
<br />
At this point you should have two running Robust.exe instances.<br />
<br />
If you put the ROBUST instances on different machines then don't forget to change the relevant service URIs in each simulator to match.<br />
<br />
Since OpenSimulator services are stateless (e.g. every request is unconnected with other requests as long as they affect the same persistent data where necessary), you can also load balance by starting more than one ROBUST instance with a copy of the same service (e.g. multiple asset services using the same database). Requests would be round-robined between the service copies using an HTTP reverse proxy implementation (e.g. nginx). See [[Performance#Services]] for more details.<br />
<br />
== Notes ==<br />
[http://opensimulator.org/pipermail/opensim-users/2012-October/011099.html Useful discussion about separating Robust instances]<br />
<br />
= Attaching your sim to someone else's grid =<br />
* Make sure you understand, agree and will enforce that grid Terms of Service<br />
* Follow the instructions of that grid owner!<br />
<br />
To set up the region server (i.e., <tt>OpenSim.exe</tt>) to connect to an external grid, follow the [[Configuration#Step 2: Configure an OpenSim.exe to use the ROBUST services]] instructions above.<br />
<br />
The grid will have already provided with the required services. In step 2 you will need to use the provided URLs for their services.<br />
<br />
In your bin/Regions.ini file (or other region config file) you will also need to set the grid co-ordinates to your regions provided from the grid operator. See [[Configuring Regions]] for more information.<br />
<br />
= Running an OpenSimulator standalone or grid installation with Hypergrid enabled =<br />
[[Hypergrid]] is an emerging architecture supported by OpenSimulator that allows a user with an account on one standalone or grid to visit other Hypergrid-enabled standalones or grids, and for users from those grids to visit the home grid. This does not require the two installations to share a central set of data services (assets, inventory, etc.). Please see [[Installing and Running Hypergrid]] for more details.<br />
<br />
= Further notes =<br />
<br />
== Troubleshooting ==<br />
<br />
See [[Troubleshooting]] <br />
<br />
== Running OpenSimulator in 32 bit in Windows 64bit==<br />
<br />
Windows 64Bit can run several instances of OpenSim in 32Bit mode, allowing each to use up to about 2GB of total physical ram.<br />
<br />
This is almost the total memory available on 32Bit versions of Windows, for all the programs (around 3GB)<br />
<br />
In 32Bit mode, the simulator uses less memory and may be a bit faster, so unless you have very large regions, you can ran it in 32Bit mode, just run:<br />
<br />
OpenSim32.exe instead of OpenSim.exe<br />
<br />
You can also run Robust in 32Bit mode:<br />
<br />
Robust32.exe in place of Robust.exe<br />
<br />
Linux 64Bit only runs the simulator in 64Bit mode<br />
<br />
== Increasing the stack reserve level when using OpenDynamicsEngine (old ODE and ubODE) on Linux or other Unix variants ==<br />
<br />
Increase your stack reserve level with the following command;<br />
<tt>ulimit -s 262144</tt> Or, run the opensim.sh to start up OpenSimulator.<br />
<br />
== Firewalls ==<br />
Some operation systems or distributions run their own firewall by default. If you can't access to OpenSimulator from remote client, you'll need to check their settings. See [[Firewall Settings]] for details.<br />
<br />
== Legacy Configuration Information ==<br />
These are some pages containing some legacy configuration information of unknown accuracy.<br />
<br />
[[OpenSim 0.6.6 legacy configuration information]]<br />
<br />
<br />
== Additional Optional Configuration Tasks ==<br />
<br />
=== Further configure OpenSimulator ===<br />
If you've looked through OpenSim.ini.example or any other of the config files, you'll see that there's a very large number of configurable parameters spread across multiple files. See [[Configuring Simulator Parameters]] for more details about the configuration infrastructure and how settings in identically named sections (e.g. [XEngine]) are merged by OpenSimulator on loading.<br />
<br />
=== Set up a second region to run on the same simulator ===<br />
See [[Configuring Regions]].<br />
<br />
=== Run Multiple Standalone Instances of OpenSimulator on the Same Server ===<br />
For each subsequent instance of OpenSim, change the 'http_listener_port' in [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] to the value excluding 9000, and 'InternalPort' in Regions.ini to the value excluding 9000. Also, make sure your regions are using different ports, as explained in [[Configuring Regions]].<br />
<br />
=== Load region content ===<br />
You can load content onto regions by using the [[OpenSim Archives|load oar command]]. To load individual OAR files into each region, use the 'change region [regionname]' command and then 'load oar [oar-location]'.<br />
<br />
=== OpenSim.exe command line options ===<br />
OpenSim.exe has command line options which allow you to perform actions such as reading configuration files from a different directory. See [[OpenSim.exe Command Line Options]] for more details.<br />
<br />
=== Script engine ===<br />
OpenSimulator supports multiple script engines. See [[ScriptEngines]] for details. If you don't know what this means then the default script engine will be fine. In fact, recent versions of OpenSimulator only ship with one script engine, the XEngine.<br />
<br />
=== Permissions Configuration ===<br />
OpenSimulator has a quite elaborate set of permissions. See [[Permissions (Server)]] for details. By default, permissions are active on region simulators.<br />
<br />
=== Logging ===<br />
By default, OpenSimulator logs information to a file called OpenSim.log in the bin directory. See [[Logging]] for details on how to further configure this if required.<br />
<br />
=== FSAsset Service ===<br />
By default the OpenSimulator asset service will store assets in the robust database. If you expect your asset data to grow larger than 50Gb you should consider changing to the [[FSAssets Service]]. (FSAssets is a new service and is currently only available in the latest development branch)<br />
<br />
=== Set up other optional modules ===<br />
* [[IRCBridgeModule]]<br />
* [[Freeswitch Module]]<br />
* [[Offline Messaging]]<br />
* [[Enabling Groups]]<br />
* See also [[User_Documentation#Setup]]<br />
<br />
=== Configuration of Web Server and Pages ===<br />
OpenSimulator contains a web server that can serve up a variety of pages. Some which come from external files and some are generated internally.<br />
* [[External Files]]<br />
* [[Internally Generated]]<br />
<br />
= Where to go from here =<br />
<br />
* [[NAT Loopback Routers]] Router and Nat Loopback Information<br />
<br />
* [[Upgrading]] from an old OpenSimulator version to a newer one.<br />
<br />
* [[Configuring_Simulator_Parameters]] cascading configuration structure, environment variables<br />
<br />
* [[Server Commands]] for creating users and controlling the system.<br />
<br />
= References =<br />
<br />
* [http://dist.opensimulator.org/wiki/opensim-standalone.odg OpenOffice draw file for OpenSimulator standalone diagram]<br />
* [http://dist.opensimulator.org/wiki/opensim-grid-simple.odg OpenOffice draw file for OpenSimulator grid diagram]<br />
<br />
[[Category:Configuration]]</div>Aiaustinhttp://opensimulator.org/wiki/ConfigurationConfiguration2023-01-14T09:34:04Z<p>Aiaustin: /* Running OpenSimulator in Standalone mode */ format only</p>
<hr />
<div>{{Quicklinks}}<br />
<br /><br />
== OpenSimulator simulator configuration file ==<br />
The region simulator configuration is managed using a file called OpenSim.ini. This file is used regardless of whether the sim is running in standalone or grid mode. This file references some additional configuration information from the config-include/ directory. Information about the various settings is contained in the [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] file itself (or OpenSim.ini.example for reference).<br />
<br />
Please note, that the name [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] can be changed via [[OpenSim.exe Command Line Options|command line arguments]].<br />
<br />
It is also possible to distribute the inifile settings over two files. This is useful if you want to run several OpenSimulator processes where most of your settings are identical except for a few. The master file is read first, then the inifile is read. Settings given in the inifile overrule settings given in the master file. The master file has the same format and the same keywords as the inifile, so the same documentation applies.<br />
<br />
== Database ==<br />
Opensimulator supports the following database engines. Information about setting these up can be found in the OpenSim.ini.example file and the other various example files in bin/config-include. '''If you do not want to use the default SQLite configuration then you will need to setup your database before proceeding further'''. SQLite does not require further configuration. See [[Database Settings]] for the detailed settings.<br />
<br />
* '''SQLite''' (default) - a lightweight database that comes bundled with OpenSimulator and can be used without requiring any extra configuration. It is mostly intended to get you up and running quickly, not for production use. It is significantly slower than MySQL. A few features here (such as attachment persistence) have not yet been fully implemented. <br />
<br />
* '''MySQL''' (fully supported) - This is the recommended database for any use beyond experimentation or small standalone applications. The minimum MySQL version is 5.1.<br />
* '''MariaDB''' (fully supported) - A compatible alternative to MySQL. You need to make sure the selected charset is utf8mb3 (ie 3 bytes). Some instalations default to uft8mb4 (4 bytes) and that will fail<br />
<br />
* '''MSSQL''' (fairly supported) - persistence support for some recent OpenSimulator features may not yet be implemented though the vast majority of them are supported.<br />
<br />
== Standalone vs. Grid ==<br />
We recommend that you first get OpenSimulator running in standalone mode before you attempt to connect it to a grid or run your own grid. OpenSimulator will start up in standalone mode out-of-the-box on the binary distributions.<br />
<br />
An OpenSimulator configuration consists of regions (run by region simulators) and backend data services (such as user, assets and inventory management).<br />
<br />
A system running in '''standalone mode''' runs both the region simulator and all the data services in a single process when you run OpenSim.exe. In this mode you can run as many regions as you like but only on a single machine.<br />
<br />
[[Image:Opensim-standalone.png|frame|center|OpenSimulator running in standalone mode. Both simulator and services run in the same process (OpenSim.exe).]]<br />
<br />
In '''grid mode''', the data services are not part of the region server process. Instead, they are run in a separate executable called Robust.exe. A Robust shell can run all the services or they can be split amongst any number of Robust instances. This allows them to be run on entirely separate machines if necessary. In this mode, the OpenSim.exe acts solely as the region server, serving one or more regions that communicate with the separate data services. At this point you can run multiple OpenSim.exe region simulators on different machines.<br />
<br />
[[Image:Opensim-grid-simple.png|frame|center|OpenSimulator running in grid mode. In this case, all the services are being run within a Robust.exe process. Multiple copies of OpenSim.exe (usually running on different machines) all use the same set of common services.]]<br />
<br />
Running in grid mode is more complicated than running in standalone mode. It requires an understanding of UUID, X,Y location, server handshake passwords, estates and estate owners, and a couple of other settings. These require more care and patience to set up. We strongly recommend that you don't attempt this unless you are extremely patient and very technically proficient.<br />
<br />
= Running OpenSimulator in Standalone mode =<br />
<br />
Binary distributions of OpenSimulator are by default configured to run in standalone mode.<br />
<br />
However, if you build OpenSimulator from the source distribution or from the git repository then you will need to:<br />
<br />
# Change into the '''''bin''''' folder<br />
# Copy the file '''''OpenSim.ini.example''''' to '''''[[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]]'''''. This configures the 3D simulator itself.<br />
# Check the [Const] section PublicPort. It should be set to 9000 by default for a standalone.<br />
# In the '''[Architecture]''' section of '''''[[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]]''''', near the bottom of the file, uncomment the ''Standalone.ini'' line. To uncomment a line of code, remove the semi-colon (;) comment symbol preceding the line.<br />
# Change into the '''''bin/config-include''''' folder<br />
# Copy the file '''''StandaloneCommon.ini.example''''' to '''''StandaloneCommon.ini'''''. This configures the in-process data services used by the standalone configuration.<br />
# Copy the file '''''FlotsamCache.ini.example''''' to '''''FlotsamCache.ini'''''. This configures the cache services used by the standalone configuration.<br />
<br />
Running OpenSimulator is then a matter of launching OpenSim.exe. However, you need to have installed all dependencies before that. See [[Dependencies]] for details. After that, open a command prompt (for Windows users, Start menu > Run > cmd) and navigate to the Opensim /bin directory.<br />
<br />
Under '''Windows''', run:<br />
OpenSim.exe<br />
On '''Linux''' run:<br />
mono OpenSim.exe<br />
<br />
== Running for the first time ==<br />
<br />
If you're running OpenSimulator for the first time, it will ask you several questions at the console that will set up a single region for you. The configuration options you enter will be written to the bin/Regions/Regions.ini file, which you can then edit at a later date if you need to make changes.<br />
<br />
Many of the questions have defaults. Here are some explanations of the questions asked:<br />
<br />
* '''New region name'''<br />
::The name for your region. Don't leave this blank!<br />
* '''Region UUID'''<br />
::The unique ID of your region. In pretty much all cases you will want to accept the randomly generated default in the square brackets. The only time when you wouldn't is if you were trying to set up a configuration to point to pre-existing region data. But in this case you are probably better off editing the Regions.ini file directly anyway<br />
* '''Region Location'''<br />
::This is the location of the region on the grid. In standalone mode you can safely leave these as the default (1000,1000). If you were to set up additional regions later on in Regions.ini then they would need different grid co-ordinates (e.g. 1000,1001). OpenSimulator regions can be placed anywhere on a 65536 by 65536 grid, but [[Hypergrid]] enabled regions may need special consideration for region location. See [[Installing and Running Hypergrid#The 4096 Regions Limit]] for more information.<br />
* '''Internal IP address'''<br />
::In virtually all cases this can be left as 0.0.0.0 (this is a wildcard that allows OpenSimulator to listen for UDP connections on any of the server's network interfaces). If you want to restrict UDP connections to only one network interface then you can specify an explicit IP address. This address is only used internally - the '''External host name''' is the one that is actually passed to the viewer (and hence is the important one).<br />
* '''Internal port'''<br />
::This is the IP port for all incoming client connections. The name is a bit misleading since it will be used externally (by a Second Life viewer, for instance) as well as internally. You can make this any port you want, but it is safe to leave at the default 9000. Each region on your server must have a unique port.<br />
* '''Allow alternate ports'''<br />
::This is currently experimental. Please leave it at the default of False.<br />
* '''External host name'''<br />
::If you leave this at the default 'SYSTEMIP' then this will become the LAN network address of the machine (e.g. 192.168.1.2). This is fine if you are connecting only from within your LAN. If you want to connect to it from a client on the internet, this should be the External IP Address of your router. Fully Qualified Domain Names (FQDNs) can also be used though they will be converted to a numeric IP address before being sent to the viewer.<br />
<br />
OpenSimulator will ask you to assign each region to an estate during the setup process. If an estate needs to be created then it will also ask you to assign an estate manager. In standalone mode, an estate manager can also be created during the setup process.<br />
<br />
Don't forget the account details you use to set up the master avatar (in 0.6.9) or the estate manager (in 0.7 and later). Only this user will initially be able to configure the in-world settings for your region. This is also a user account that you can use to perform your initial login test.<br />
<br />
See [[Configuring Regions]] for more information about the Regions.ini file that these questions generate.<br />
<br />
If you want to create a user other than the estate manager, then in the server console type:<br />
<br />
create user<br />
<br />
This will ask you a series of questions for creating a user (such as first name, last name and password).<br />
<br />
== Network access for the standalone installation ==<br />
In standalone mode, a viewer connecting to your installation needs the following network access to your installation machine.<br />
<br />
1. TCP over the http_listener_port as used in your simulator. This is set in the [Network] section of your [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]].<br />
<br />
<source lang="ini"><br />
[Network]<br />
http_listener_port = 9000<br />
</source><br />
<br />
This will be the default of 9000 if you have not explicitly changed it.<br />
<br />
2. UDP over each region's InternalPort as configured in your region files (such as Regions.ini). For instance, if you have configured<br />
<br />
<source lang="ini"><br />
[test]<br />
RegionUUID = dd5b77f8-bf88-45ac-aace-35bd76426c81<br />
Location = 1000,1000<br />
SizeX = 256<br />
SizeY = 256<br />
SizeZ = 256<br />
InternalAddress = 0.0.0.0<br />
InternalPort = 9000<br />
AllowAlternatePorts = False<br />
ExternalHostName = mygrid.com<br />
<br />
[test2]<br />
RegionUUID = dd5b77f8-bf88-45ac-aace-35bd76426c82<br />
Location = 1000,1001<br />
SizeX = 256<br />
SizeY = 256<br />
SizeZ = 256<br />
InternalAddress = 0.0.0.0<br />
InternalPort = 9001<br />
AllowAlternatePorts = False<br />
ExternalHostName = mygrid.com<br />
</source><br />
<br />
then you will need to open ports 9000 and 9001 to UDP traffic.<br />
<br />
3. The network address of the machine hosting the OpenSimulator installation must be accessible to connecting viewers. In the example above, the installation machine is reachable from the Internet via the domain name "mygrid.com". If the same installation needs to be accessed by viewers on the same network, it must be possible for them to also successfully resolve that domain name (not all routers, especially home routers, have this "loopback capability").<br />
<br />
If the installation only needed to be accessed on the same LAN, then one could you the local IP address of the server (e.g. 192.168.1.2).<br />
<br />
== Connecting to a standalone installation ==<br />
<br />
To connect to your new sim with your user, start up a viewer with the following command line switches:<br />
<br />
'''Client on same machine as OpenSim:'''<br />
-loginuri http://127.0.0.1:9000<br />
<br />
'''Client on same LAN as OpenSim:'''<br />
-loginuri http://lan_ip:9000<br />
<br />
'''Client on different machine or internet:'''<br />
-loginuri http://external_ip:9000<br />
<br />
Then enter the user name and password you set up in the previous step and your new user should login.<br />
<br />
Be aware of [http://osgrid.org/forums/viewtopic.php?f=5&t=400&start=0&st=0&sk=t&sd=a loopback] problems when Running viewer &amp; server(s) on the same machine (LAN) by using the "external" configuration. (<u>'''You might notice endless waiting for region handshake'''</u>.) See also [[Troubleshooting|troubleshoot hints]]. If you're having Connectivity problems, [[Network Settings|be sure to read the Network Configuration Page]]. This is important if you see Region handshake issue.<br />
<br />
[[Image:Exclamation.png|left]]<br />
<br />
== IMPORTANT NOTE, DIVA DISTRO - 4 Apr. 2012 - ==<br />
<br />
'''If you download the latest version of diva-r18611.tar.bz''', it is necessary to first launch the setup program ''configure.exe''<br />
*In Linux or MacOSX : open a terminal and enter "mono /diva-r18611/bin/Configure.exe" (assuming that you have placed the Diva distro in /diva-r18611)<br />
*In Windows, assuming they extracted Diva in My Documents, one would open "Run => cmd" and enter '''cd "%USERPROFILE%\My Documents\diva-r18611\"'', followed by "Configure.exe". <br />
After issuing the command, you can set your sim's domain name, and carefully answer the program's questions, then start the program as instructed in above paragraphs.<br />
<br />
The program will install the optimum configuration for OpenSim, example: '''<nowiki>http://<your_IP>:9000</nowiki>''' and WiFi '''<nowiki>http:<your_IP>:9000/wifi</nowiki>''' <br />
In the standalone version, four regions will be set up. You can optionally add other regions later on, so make sure to use the same first name with the addition of a number <br />
(ex: "region 5", "region 6", "region 7", etc. otherwise you can't enter the region and you'd be placed in the nearest free location.<br />
<br />
If you wish to enter a different region name, make sure that the "distance" between the island created by the Wifi configuration program and the next, <br />
will be at least 40 positions away from the first installed region)<br />
(command console: create region Johnnyland RegionConfigure.ini)<br />
<br />
= Running OpenSimulator in Grid mode =<br />
<br />
Running OpenSimulator in grid mode is considerably more complicated than running a standalone instance. Instead of running everything in the same process, backend data services (asset, inventory, etc.) run in one or more separate processes, often on a different machine. This allows multiple OpenSim.exe simulator instances to use the same asset and inventory data.<br />
<br />
== Step 1: Set up a ROBUST services instance ==<br />
<br />
1. In the bin directory, copy Robust.ini.example to Robust.ini. The example file is configured to run all the services in a single ROBUST instance.<br />
<br />
2. Configure the [[Database Settings]] in Robust.ini to use your MySQL database. Only MySQL is supported for running grid services. <br />
<br />
3. Start up Robust.exe. <br />
<br />
mono Robust.exe (Linux, BSD, Mac OS X)<br />
<br />
or<br />
<br />
Robust.exe (Windows)<br />
<br />
If you don't see any errors (in red) on the console then you can move on to the next step.<br />
<br />
4. Every region must belong to an estate, and every estate must have an owner which is a valid user account in OpenSim's user account service. Create a user on the ROBUST command console with the following command.<br />
<br />
create user<br />
<br />
This will ask you for the user's name, password and an optional e-mail. Remember this name since you will need it when you start up the simulator for the first time.<br />
<br />
== Step 2: Configure an OpenSim.exe to use the ROBUST services ==<br />
<br />
In grid mode, as in standalone mode, you need to configure [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] which controls the 3D simulator itself.<br />
<br />
However, instead of using and configuring the file config-include/StandaloneCommon.ini, a simulator connecting to a grid needs to use and configure the config-include/GridCommon.ini file, in order to connect to the ROBUST hosted remote data services rather than in-process local ones.<br />
<br />
The steps for both these operations are as follows.<br />
<br />
1. Copy bin/OpenSim.ini.example to [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]]<br />
<br />
2. Find the [Architecture] section at the very bottom of OpenSim.ini. Make sure that one of the following lines is uncommented:<br />
<br />
Include-Architecture = "config-include/Grid.ini" (in OpenSimulator 0.7.1 and later)<br />
<br />
or<br />
<br />
Include-Grid = "config-include/Grid.ini" (in OpenSimulator 0.7.0.2 and earlier)<br />
<br />
The others should remain commented.<br />
<br />
3. Go to bin/config-include and copy GridCommon.ini.example to GridCommon.ini.<br />
<br />
4. Open GridCommon.ini in a text editor. You will see lots of URL entries, each of which have dummy defaults of http://myassetserver.com:8003, http://myinventoryserver.com:8003, etc. You will need to change each of these to point towards the address of your ROBUST instance. For instance, if you're running ROBUST on a machine with a local IP address of 192.168.1.2, you will need to change AssetServerURI to the setting<br />
<br />
AssetServerURI = "http://192.168.1.2:8003"<br />
<br />
5. Run OpenSim.exe. If you're running OpenSim.exe for the first time you will get the same questions about setting up the region that occur on a first-run in standalone mode. Please see the standalone section for instructions on how to answer these, or read more information about the Regions.ini file on the [[Configuring Regions]] page.<br />
<br />
If everything is set up correctly, when starting up OpenSim.exe you shouldn't see any errors. You should also see the ROBUST console display log lines saying that the region has registered with the grid service. For example,<br />
<br />
21:43:45 - [GRID SERVICE]: Region t1 (176cc95e-f693-4b02-8e08-af86e2372faa) registered successfully at 256000-256000<br />
21:43:47 - [GRID SERVICE]: region t1 has 0 neighbours<br />
<br />
== Network access for a grid installation ==<br />
In standalone mode, a viewer connecting to your installation needs to access<br />
<br />
# The login service over TCP and other configured public services (e.g. grid info, map).<br />
# The http_server_port of each configured simulator over TCP.<br />
# The InternalPort and ExternalHostName of each configured region.<br />
<br />
The last two requirements are the same as for standalone installations, except that they apply to each server that hosts a simulator. Please see the standalone section above for more details.<br />
<br />
The login service is a little different. Whereas in standalone this uses the same http_server_port as the simulator itself, in grid mode it's running in a separate ROBUST service.<br />
<br />
The default port for the login service is 8002. You can see this used in the [ServiceList] section of Robust.ini.example.<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector"<br />
InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector"<br />
GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector"<br />
GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector"<br />
AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector"<br />
OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector"<br />
AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector"<br />
LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector"<br />
PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector"<br />
UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector"<br />
GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector"<br />
FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector"<br />
MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector"<br />
MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"<br />
</source><br />
<br />
Here all the public services (those where the viewer connects directly to the service) are served on port 8002, with internal services on 8003. So you need to make sure that the viewer can access port 8002 over TCP but you do not want to expose port 8003. Only simulators need to be able to connect to port 8003.<br />
<br />
== Connecting to a grid installation ==<br />
<br />
Your client startup line will look something like<br />
<br />
-loginuri http://mygrid.com:8002<br />
<br />
The loginuri needs to be the address to the login service. In standalone mode, this was the same address as the region simulator and the port was 9000 by default. However, in grid mode it will be the address to login service hosted on the ROBUST instance. In this case, the address will be 192.168.1.2. The port number of 8002 is the traditional one for the grid login service and is the default in Robust.ini.example.<br />
<br />
If the login is successful, you will see log lines on the ROBUST console (for the login itself) and then log lines on the region simulator console (as the login process tells the simulator to expect the avatar, tells the viewer the address of the region simulator and then when the viewer starts talking to the simulator directly).<br />
<br />
= Running multiple ROBUST service instances =<br />
<b>WARNING:<br><br />
This is a tricky issue and contrary to past allegations, opensim is not well prepared for this kind of microservices concepts.<br><br />
You can split some services by different machines, if they have no interdependencies. For example current XBakes can run anywhere<br><br />
Running several instances of same service is a invite to fail.<br><br />
Some services do assume they are the only instance, like current PresenceService, GriduserService, etc, and<br />
even do own caching to reduce load on the database engine, and that cache cannot be syncronized across instances<br><br />
Most will only service http in paralell, placing the same, and most of the load, on the same database engine. So unless that database service is a distributed system, doing proper local caching and synchronization with a master database, the gains of having multiple instances is not that large, if any if if not a total fail<br><br />
<br />
Another important thing to note is that currently several services need to read data from the configuration of other services. For example Gridservice needs to read data from its section, GridService, and sections LoginService, Hypergrid and GridInfoService. So the ini file for the robust instance running it needs to have those sections, and of course updated. <br />
</b><br><br />
<br />
<br />
If you are operating a grid, then you can run different services (e.g. asset, inventory) in different ROBUST instances, in order to spread the load. To do this, you will need to edit the ServiceConnectors parameter in the [Startup] section of Robust.ini (or [[Configuration/files/Robust/Robust.HG.ini|Robust.HG.ini]] if you're running Hypergrid). The default ServiceConnectors parameter looks something like this<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector"<br />
InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector"<br />
GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector"<br />
GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector"<br />
AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector"<br />
OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector"<br />
AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector"<br />
LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector"<br />
PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector"<br />
UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector"<br />
GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector"<br />
FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector"<br />
MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector"<br />
MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"<br />
</source><br />
<br />
Each entry has the form <br />
<br />
<port-number>/<dll>:<connector-class-name><br />
<br />
For instance, the first entry above<br />
<br />
8003/OpenSim.Server.Handlers.dll:AssetServiceConnector<br />
<br />
says to start an AssetServiceConnector (and hence an asset service) from the OpenSim.Server.Handlers.dll and to server that from port 8003.<br />
<br />
By default, Robust.exe loads a configuration file with the same name but with .ini appended instead of .exe. So Robust.exe will look for an inifile called Robust.ini. You can change this by giving the parameter on the commandline. For instance, to start Robust with HG parameters, one would use<br />
<br />
Robust.exe -inifile=Robust.HG.ini<br />
<br />
So if you wanted to run every connector apart from assets in one instance of ROBUST and the asset connector in another instance, you would have two ini files. One could remain Robust.ini and have<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector"<br />
GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector"<br />
GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector"<br />
AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector"<br />
OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector"<br />
AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector"<br />
LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector"<br />
PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector"<br />
UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector"<br />
GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector"<br />
FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector"<br />
MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector"<br />
MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"<br />
</source><br />
<br />
The other could be called Robust.Assets.ini and have<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
AssetServiceConnector = "8004/OpenSim.Server.Handlers.dll:AssetServiceConnector"<br />
</source><br />
<br />
Note that this is using port 8004 instead of port 8003. This is necessary since only one executable can use each port at a time. You will need to make sure your simulator configuration files use port 8004 for the asset service as well.<br />
<br />
You will also need to change the default network port to 8004 for this second copy of Robust.exe<br />
<br />
<pre><br />
[Network]<br />
port = 8004<br />
</pre><br />
<br />
Once you've created the two ROBUST configuration files (Robust.ini containing all services apart from asset and Robust.Assets.ini containing only the asset service), then you could start the first Robust.exe as usual.<br />
<br />
Robust.exe<br />
<br />
This will load Robust.ini, as we haven't specified a Robust.ini. Also, the logfile it will use will be Robust.log as we haven't manually specified one.<br />
<br />
The second ROBUST instance we would start with<br />
<br />
Robust.exe -inifile=Robust.Assets.ini -logfile=Robust.Assets.log<br />
<br />
The -inifile switch tells the second Robust instance to load it's configuration from Robust.Assets.ini rather than Robust.ini. The -logfile switch tells Robust.exe to use Robust.Assets.log as its logfile rather than the default Robust.log. If you don't specify this switch then you may see errors on the console about a locked log file.<br />
<br />
At this point you should have two running Robust.exe instances.<br />
<br />
If you put the ROBUST instances on different machines then don't forget to change the relevant service URIs in each simulator to match.<br />
<br />
Since OpenSimulator services are stateless (e.g. every request is unconnected with other requests as long as they affect the same persistent data where necessary), you can also load balance by starting more than one ROBUST instance with a copy of the same service (e.g. multiple asset services using the same database). Requests would be round-robined between the service copies using an HTTP reverse proxy implementation (e.g. nginx). See [[Performance#Services]] for more details.<br />
<br />
== Notes ==<br />
[http://opensimulator.org/pipermail/opensim-users/2012-October/011099.html Useful discussion about separating Robust instances]<br />
<br />
= Attaching your sim to someone else's grid =<br />
* Make sure you understand, agree and will enforce that grid Terms of Service<br />
* Follow the instructions of that grid owner!<br />
<br />
To set up the region server (i.e., <tt>OpenSim.exe</tt>) to connect to an external grid, follow the [[Configuration#Step 2: Configure an OpenSim.exe to use the ROBUST services]] instructions above.<br />
<br />
The grid will have already provided with the required services. In step 2 you will need to use the provided URLs for their services.<br />
<br />
In your bin/Regions.ini file (or other region config file) you will also need to set the grid co-ordinates to your regions provided from the grid operator. See [[Configuring Regions]] for more information.<br />
<br />
= Running an OpenSimulator standalone or grid installation with Hypergrid enabled =<br />
[[Hypergrid]] is an emerging architecture supported by OpenSimulator that allows a user with an account on one standalone or grid to visit other Hypergrid-enabled standalones or grids, and for users from those grids to visit the home grid. This does not require the two installations to share a central set of data services (assets, inventory, etc.). Please see [[Installing and Running Hypergrid]] for more details.<br />
<br />
= Further notes =<br />
<br />
== Troubleshooting ==<br />
<br />
See [[Troubleshooting]] <br />
<br />
== Running OpenSimulator in 32 bit in Windows 64bit==<br />
<br />
Windows 64Bit can run several instances of OpenSim in 32Bit mode, allowing each to use up to about 2GB of total physical ram.<br />
<br />
This is almost the total memory available on 32Bit versions of Windows, for all the programs (around 3GB)<br />
<br />
In 32Bit mode, the simulator uses less memory and may be a bit faster, so unless you have very large regions, you can ran it in 32Bit mode, just run:<br />
<br />
OpenSim32.exe instead of OpenSim.exe<br />
<br />
You can also run Robust in 32Bit mode:<br />
<br />
Robust32.exe in place of Robust.exe<br />
<br />
Linux 64Bit only runs the simulator in 64Bit mode<br />
<br />
== Increasing the stack reserve level when using OpenDynamicsEngine (old ODE and ubODE) on Linux or other Unix variants ==<br />
<br />
Increase your stack reserve level with the following command;<br />
<tt>ulimit -s 262144</tt> Or, run the opensim.sh to start up OpenSimulator.<br />
<br />
== Firewalls ==<br />
Some operation systems or distributions run their own firewall by default. If you can't access to OpenSimulator from remote client, you'll need to check their settings. See [[Firewall Settings]] for details.<br />
<br />
== Legacy Configuration Information ==<br />
These are some pages containing some legacy configuration information of unknown accuracy.<br />
<br />
[[OpenSim 0.6.6 legacy configuration information]]<br />
<br />
<br />
== Additional Optional Configuration Tasks ==<br />
<br />
=== Further configure OpenSimulator ===<br />
If you've looked through OpenSim.ini.example or any other of the config files, you'll see that there's a very large number of configurable parameters spread across multiple files. See [[Configuring Simulator Parameters]] for more details about the configuration infrastructure and how settings in identically named sections (e.g. [XEngine]) are merged by OpenSimulator on loading.<br />
<br />
=== Set up a second region to run on the same simulator ===<br />
See [[Configuring Regions]].<br />
<br />
=== Run Multiple Standalone Instances of OpenSimulator on the Same Server ===<br />
For each subsequent instance of OpenSim, change the 'http_listener_port' in [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] to the value excluding 9000, and 'InternalPort' in Regions.ini to the value excluding 9000. Also, make sure your regions are using different ports, as explained in [[Configuring Regions]].<br />
<br />
=== Load region content ===<br />
You can load content onto regions by using the [[OpenSim Archives|load oar command]]. To load individual OAR files into each region, use the 'change region [regionname]' command and then 'load oar [oar-location]'.<br />
<br />
=== OpenSim.exe command line options ===<br />
OpenSim.exe has command line options which allow you to perform actions such as reading configuration files from a different directory. See [[OpenSim.exe Command Line Options]] for more details.<br />
<br />
=== Script engine ===<br />
OpenSimulator supports multiple script engines. See [[ScriptEngines]] for details. If you don't know what this means then the default script engine will be fine. In fact, recent versions of OpenSimulator only ship with one script engine, the XEngine.<br />
<br />
=== Permissions Configuration ===<br />
OpenSimulator has a quite elaborate set of permissions. See [[Permissions (Server)]] for details. By default, permissions are active on region simulators.<br />
<br />
=== Logging ===<br />
By default, OpenSimulator logs information to a file called OpenSim.log in the bin directory. See [[Logging]] for details on how to further configure this if required.<br />
<br />
=== FSAsset Service ===<br />
By default the OpenSimulator asset service will store assets in the robust database. If you expect your asset data to grow larger than 50Gb you should consider changing to the [[FSAssets Service]]. (FSAssets is a new service and is currently only available in the latest development branch)<br />
<br />
=== Set up other optional modules ===<br />
* [[IRCBridgeModule]]<br />
* [[Freeswitch Module]]<br />
* [[Offline Messaging]]<br />
* [[Enabling Groups]]<br />
* See also [[User_Documentation#Setup]]<br />
<br />
=== Configuration of Web Server and Pages ===<br />
OpenSimulator contains a web server that can serve up a variety of pages. Some which come from external files and some are generated internally.<br />
* [[External Files]]<br />
* [[Internally Generated]]<br />
<br />
= Where to go from here =<br />
<br />
* [[NAT Loopback Routers]] Router and Nat Loopback Information<br />
<br />
* [[Upgrading]] from an old OpenSimulator version to a newer one.<br />
<br />
* [[Configuring_Simulator_Parameters]] cascading configuration structure, environment variables<br />
<br />
* [[Server Commands]] for creating users and controlling the system.<br />
<br />
= References =<br />
<br />
* [http://dist.opensimulator.org/wiki/opensim-standalone.odg OpenOffice draw file for OpenSimulator standalone diagram]<br />
* [http://dist.opensimulator.org/wiki/opensim-grid-simple.odg OpenOffice draw file for OpenSimulator grid diagram]<br />
<br />
[[Category:Configuration]]</div>Aiaustinhttp://opensimulator.org/wiki/ConfigurationConfiguration2023-01-14T09:33:14Z<p>Aiaustin: /* Running OpenSimulator in Standalone mode */ Include check that OpenSim.ini PublicPort is set to 9000 for Standalones.</p>
<hr />
<div>{{Quicklinks}}<br />
<br /><br />
== OpenSimulator simulator configuration file ==<br />
The region simulator configuration is managed using a file called OpenSim.ini. This file is used regardless of whether the sim is running in standalone or grid mode. This file references some additional configuration information from the config-include/ directory. Information about the various settings is contained in the [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] file itself (or OpenSim.ini.example for reference).<br />
<br />
Please note, that the name [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] can be changed via [[OpenSim.exe Command Line Options|command line arguments]].<br />
<br />
It is also possible to distribute the inifile settings over two files. This is useful if you want to run several OpenSimulator processes where most of your settings are identical except for a few. The master file is read first, then the inifile is read. Settings given in the inifile overrule settings given in the master file. The master file has the same format and the same keywords as the inifile, so the same documentation applies.<br />
<br />
== Database ==<br />
Opensimulator supports the following database engines. Information about setting these up can be found in the OpenSim.ini.example file and the other various example files in bin/config-include. '''If you do not want to use the default SQLite configuration then you will need to setup your database before proceeding further'''. SQLite does not require further configuration. See [[Database Settings]] for the detailed settings.<br />
<br />
* '''SQLite''' (default) - a lightweight database that comes bundled with OpenSimulator and can be used without requiring any extra configuration. It is mostly intended to get you up and running quickly, not for production use. It is significantly slower than MySQL. A few features here (such as attachment persistence) have not yet been fully implemented. <br />
<br />
* '''MySQL''' (fully supported) - This is the recommended database for any use beyond experimentation or small standalone applications. The minimum MySQL version is 5.1.<br />
* '''MariaDB''' (fully supported) - A compatible alternative to MySQL. You need to make sure the selected charset is utf8mb3 (ie 3 bytes). Some instalations default to uft8mb4 (4 bytes) and that will fail<br />
<br />
* '''MSSQL''' (fairly supported) - persistence support for some recent OpenSimulator features may not yet be implemented though the vast majority of them are supported.<br />
<br />
== Standalone vs. Grid ==<br />
We recommend that you first get OpenSimulator running in standalone mode before you attempt to connect it to a grid or run your own grid. OpenSimulator will start up in standalone mode out-of-the-box on the binary distributions.<br />
<br />
An OpenSimulator configuration consists of regions (run by region simulators) and backend data services (such as user, assets and inventory management).<br />
<br />
A system running in '''standalone mode''' runs both the region simulator and all the data services in a single process when you run OpenSim.exe. In this mode you can run as many regions as you like but only on a single machine.<br />
<br />
[[Image:Opensim-standalone.png|frame|center|OpenSimulator running in standalone mode. Both simulator and services run in the same process (OpenSim.exe).]]<br />
<br />
In '''grid mode''', the data services are not part of the region server process. Instead, they are run in a separate executable called Robust.exe. A Robust shell can run all the services or they can be split amongst any number of Robust instances. This allows them to be run on entirely separate machines if necessary. In this mode, the OpenSim.exe acts solely as the region server, serving one or more regions that communicate with the separate data services. At this point you can run multiple OpenSim.exe region simulators on different machines.<br />
<br />
[[Image:Opensim-grid-simple.png|frame|center|OpenSimulator running in grid mode. In this case, all the services are being run within a Robust.exe process. Multiple copies of OpenSim.exe (usually running on different machines) all use the same set of common services.]]<br />
<br />
Running in grid mode is more complicated than running in standalone mode. It requires an understanding of UUID, X,Y location, server handshake passwords, estates and estate owners, and a couple of other settings. These require more care and patience to set up. We strongly recommend that you don't attempt this unless you are extremely patient and very technically proficient.<br />
<br />
= Running OpenSimulator in Standalone mode =<br />
<br />
Binary distributions of OpenSimulator are by default configured to run in standalone mode.<br />
<br />
However, if you build OpenSimulator from the source distribution or from the git repository then you will need to:<br />
<br />
# Change into the '''''bin''''' folder<br />
# Copy the file '''''OpenSim.ini.example''''' to '''''[[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]]'''''. This configures the 3D simulator itself.<br />
# Check the [Const] section PublicPort. It should be set to 9000 by default for a standalone.<br />
# In the '''[Architecture]''' section of '''''[[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]]''''', near the bottom of the file, uncomment the ''Standalone.ini'' line. To uncomment a line of code, remove the semi-colon (;) comment symbol preceding the line so that it says:<br />
<br />
Include-Architecture = "config-include/Standalone.ini"<br />
<br />
# Change into the '''''bin/config-include''''' folder<br />
# Copy the file '''''StandaloneCommon.ini.example''''' to '''''StandaloneCommon.ini'''''. This configures the in-process data services used by the standalone configuration.<br />
# Copy the file '''''FlotsamCache.ini.example''''' to '''''FlotsamCache.ini'''''. This configures the cache services used by the standalone configuration.<br />
<br />
Running OpenSimulator is then a matter of launching OpenSim.exe. However, you need to have installed all dependencies before that. See [[Dependencies]] for details. After that, open a command prompt (for Windows users, Start menu > Run > cmd) and navigate to the Opensim /bin directory.<br />
<br />
Under '''Windows''', run:<br />
OpenSim.exe<br />
On '''Linux''' run:<br />
mono OpenSim.exe<br />
<br />
== Running for the first time ==<br />
<br />
If you're running OpenSimulator for the first time, it will ask you several questions at the console that will set up a single region for you. The configuration options you enter will be written to the bin/Regions/Regions.ini file, which you can then edit at a later date if you need to make changes.<br />
<br />
Many of the questions have defaults. Here are some explanations of the questions asked:<br />
<br />
* '''New region name'''<br />
::The name for your region. Don't leave this blank!<br />
* '''Region UUID'''<br />
::The unique ID of your region. In pretty much all cases you will want to accept the randomly generated default in the square brackets. The only time when you wouldn't is if you were trying to set up a configuration to point to pre-existing region data. But in this case you are probably better off editing the Regions.ini file directly anyway<br />
* '''Region Location'''<br />
::This is the location of the region on the grid. In standalone mode you can safely leave these as the default (1000,1000). If you were to set up additional regions later on in Regions.ini then they would need different grid co-ordinates (e.g. 1000,1001). OpenSimulator regions can be placed anywhere on a 65536 by 65536 grid, but [[Hypergrid]] enabled regions may need special consideration for region location. See [[Installing and Running Hypergrid#The 4096 Regions Limit]] for more information.<br />
* '''Internal IP address'''<br />
::In virtually all cases this can be left as 0.0.0.0 (this is a wildcard that allows OpenSimulator to listen for UDP connections on any of the server's network interfaces). If you want to restrict UDP connections to only one network interface then you can specify an explicit IP address. This address is only used internally - the '''External host name''' is the one that is actually passed to the viewer (and hence is the important one).<br />
* '''Internal port'''<br />
::This is the IP port for all incoming client connections. The name is a bit misleading since it will be used externally (by a Second Life viewer, for instance) as well as internally. You can make this any port you want, but it is safe to leave at the default 9000. Each region on your server must have a unique port.<br />
* '''Allow alternate ports'''<br />
::This is currently experimental. Please leave it at the default of False.<br />
* '''External host name'''<br />
::If you leave this at the default 'SYSTEMIP' then this will become the LAN network address of the machine (e.g. 192.168.1.2). This is fine if you are connecting only from within your LAN. If you want to connect to it from a client on the internet, this should be the External IP Address of your router. Fully Qualified Domain Names (FQDNs) can also be used though they will be converted to a numeric IP address before being sent to the viewer.<br />
<br />
OpenSimulator will ask you to assign each region to an estate during the setup process. If an estate needs to be created then it will also ask you to assign an estate manager. In standalone mode, an estate manager can also be created during the setup process.<br />
<br />
Don't forget the account details you use to set up the master avatar (in 0.6.9) or the estate manager (in 0.7 and later). Only this user will initially be able to configure the in-world settings for your region. This is also a user account that you can use to perform your initial login test.<br />
<br />
See [[Configuring Regions]] for more information about the Regions.ini file that these questions generate.<br />
<br />
If you want to create a user other than the estate manager, then in the server console type:<br />
<br />
create user<br />
<br />
This will ask you a series of questions for creating a user (such as first name, last name and password).<br />
<br />
== Network access for the standalone installation ==<br />
In standalone mode, a viewer connecting to your installation needs the following network access to your installation machine.<br />
<br />
1. TCP over the http_listener_port as used in your simulator. This is set in the [Network] section of your [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]].<br />
<br />
<source lang="ini"><br />
[Network]<br />
http_listener_port = 9000<br />
</source><br />
<br />
This will be the default of 9000 if you have not explicitly changed it.<br />
<br />
2. UDP over each region's InternalPort as configured in your region files (such as Regions.ini). For instance, if you have configured<br />
<br />
<source lang="ini"><br />
[test]<br />
RegionUUID = dd5b77f8-bf88-45ac-aace-35bd76426c81<br />
Location = 1000,1000<br />
SizeX = 256<br />
SizeY = 256<br />
SizeZ = 256<br />
InternalAddress = 0.0.0.0<br />
InternalPort = 9000<br />
AllowAlternatePorts = False<br />
ExternalHostName = mygrid.com<br />
<br />
[test2]<br />
RegionUUID = dd5b77f8-bf88-45ac-aace-35bd76426c82<br />
Location = 1000,1001<br />
SizeX = 256<br />
SizeY = 256<br />
SizeZ = 256<br />
InternalAddress = 0.0.0.0<br />
InternalPort = 9001<br />
AllowAlternatePorts = False<br />
ExternalHostName = mygrid.com<br />
</source><br />
<br />
then you will need to open ports 9000 and 9001 to UDP traffic.<br />
<br />
3. The network address of the machine hosting the OpenSimulator installation must be accessible to connecting viewers. In the example above, the installation machine is reachable from the Internet via the domain name "mygrid.com". If the same installation needs to be accessed by viewers on the same network, it must be possible for them to also successfully resolve that domain name (not all routers, especially home routers, have this "loopback capability").<br />
<br />
If the installation only needed to be accessed on the same LAN, then one could you the local IP address of the server (e.g. 192.168.1.2).<br />
<br />
== Connecting to a standalone installation ==<br />
<br />
To connect to your new sim with your user, start up a viewer with the following command line switches:<br />
<br />
'''Client on same machine as OpenSim:'''<br />
-loginuri http://127.0.0.1:9000<br />
<br />
'''Client on same LAN as OpenSim:'''<br />
-loginuri http://lan_ip:9000<br />
<br />
'''Client on different machine or internet:'''<br />
-loginuri http://external_ip:9000<br />
<br />
Then enter the user name and password you set up in the previous step and your new user should login.<br />
<br />
Be aware of [http://osgrid.org/forums/viewtopic.php?f=5&t=400&start=0&st=0&sk=t&sd=a loopback] problems when Running viewer &amp; server(s) on the same machine (LAN) by using the "external" configuration. (<u>'''You might notice endless waiting for region handshake'''</u>.) See also [[Troubleshooting|troubleshoot hints]]. If you're having Connectivity problems, [[Network Settings|be sure to read the Network Configuration Page]]. This is important if you see Region handshake issue.<br />
<br />
[[Image:Exclamation.png|left]]<br />
<br />
== IMPORTANT NOTE, DIVA DISTRO - 4 Apr. 2012 - ==<br />
<br />
'''If you download the latest version of diva-r18611.tar.bz''', it is necessary to first launch the setup program ''configure.exe''<br />
*In Linux or MacOSX : open a terminal and enter "mono /diva-r18611/bin/Configure.exe" (assuming that you have placed the Diva distro in /diva-r18611)<br />
*In Windows, assuming they extracted Diva in My Documents, one would open "Run => cmd" and enter '''cd "%USERPROFILE%\My Documents\diva-r18611\"'', followed by "Configure.exe". <br />
After issuing the command, you can set your sim's domain name, and carefully answer the program's questions, then start the program as instructed in above paragraphs.<br />
<br />
The program will install the optimum configuration for OpenSim, example: '''<nowiki>http://<your_IP>:9000</nowiki>''' and WiFi '''<nowiki>http:<your_IP>:9000/wifi</nowiki>''' <br />
In the standalone version, four regions will be set up. You can optionally add other regions later on, so make sure to use the same first name with the addition of a number <br />
(ex: "region 5", "region 6", "region 7", etc. otherwise you can't enter the region and you'd be placed in the nearest free location.<br />
<br />
If you wish to enter a different region name, make sure that the "distance" between the island created by the Wifi configuration program and the next, <br />
will be at least 40 positions away from the first installed region)<br />
(command console: create region Johnnyland RegionConfigure.ini)<br />
<br />
= Running OpenSimulator in Grid mode =<br />
<br />
Running OpenSimulator in grid mode is considerably more complicated than running a standalone instance. Instead of running everything in the same process, backend data services (asset, inventory, etc.) run in one or more separate processes, often on a different machine. This allows multiple OpenSim.exe simulator instances to use the same asset and inventory data.<br />
<br />
== Step 1: Set up a ROBUST services instance ==<br />
<br />
1. In the bin directory, copy Robust.ini.example to Robust.ini. The example file is configured to run all the services in a single ROBUST instance.<br />
<br />
2. Configure the [[Database Settings]] in Robust.ini to use your MySQL database. Only MySQL is supported for running grid services. <br />
<br />
3. Start up Robust.exe. <br />
<br />
mono Robust.exe (Linux, BSD, Mac OS X)<br />
<br />
or<br />
<br />
Robust.exe (Windows)<br />
<br />
If you don't see any errors (in red) on the console then you can move on to the next step.<br />
<br />
4. Every region must belong to an estate, and every estate must have an owner which is a valid user account in OpenSim's user account service. Create a user on the ROBUST command console with the following command.<br />
<br />
create user<br />
<br />
This will ask you for the user's name, password and an optional e-mail. Remember this name since you will need it when you start up the simulator for the first time.<br />
<br />
== Step 2: Configure an OpenSim.exe to use the ROBUST services ==<br />
<br />
In grid mode, as in standalone mode, you need to configure [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] which controls the 3D simulator itself.<br />
<br />
However, instead of using and configuring the file config-include/StandaloneCommon.ini, a simulator connecting to a grid needs to use and configure the config-include/GridCommon.ini file, in order to connect to the ROBUST hosted remote data services rather than in-process local ones.<br />
<br />
The steps for both these operations are as follows.<br />
<br />
1. Copy bin/OpenSim.ini.example to [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]]<br />
<br />
2. Find the [Architecture] section at the very bottom of OpenSim.ini. Make sure that one of the following lines is uncommented:<br />
<br />
Include-Architecture = "config-include/Grid.ini" (in OpenSimulator 0.7.1 and later)<br />
<br />
or<br />
<br />
Include-Grid = "config-include/Grid.ini" (in OpenSimulator 0.7.0.2 and earlier)<br />
<br />
The others should remain commented.<br />
<br />
3. Go to bin/config-include and copy GridCommon.ini.example to GridCommon.ini.<br />
<br />
4. Open GridCommon.ini in a text editor. You will see lots of URL entries, each of which have dummy defaults of http://myassetserver.com:8003, http://myinventoryserver.com:8003, etc. You will need to change each of these to point towards the address of your ROBUST instance. For instance, if you're running ROBUST on a machine with a local IP address of 192.168.1.2, you will need to change AssetServerURI to the setting<br />
<br />
AssetServerURI = "http://192.168.1.2:8003"<br />
<br />
5. Run OpenSim.exe. If you're running OpenSim.exe for the first time you will get the same questions about setting up the region that occur on a first-run in standalone mode. Please see the standalone section for instructions on how to answer these, or read more information about the Regions.ini file on the [[Configuring Regions]] page.<br />
<br />
If everything is set up correctly, when starting up OpenSim.exe you shouldn't see any errors. You should also see the ROBUST console display log lines saying that the region has registered with the grid service. For example,<br />
<br />
21:43:45 - [GRID SERVICE]: Region t1 (176cc95e-f693-4b02-8e08-af86e2372faa) registered successfully at 256000-256000<br />
21:43:47 - [GRID SERVICE]: region t1 has 0 neighbours<br />
<br />
== Network access for a grid installation ==<br />
In standalone mode, a viewer connecting to your installation needs to access<br />
<br />
# The login service over TCP and other configured public services (e.g. grid info, map).<br />
# The http_server_port of each configured simulator over TCP.<br />
# The InternalPort and ExternalHostName of each configured region.<br />
<br />
The last two requirements are the same as for standalone installations, except that they apply to each server that hosts a simulator. Please see the standalone section above for more details.<br />
<br />
The login service is a little different. Whereas in standalone this uses the same http_server_port as the simulator itself, in grid mode it's running in a separate ROBUST service.<br />
<br />
The default port for the login service is 8002. You can see this used in the [ServiceList] section of Robust.ini.example.<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector"<br />
InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector"<br />
GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector"<br />
GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector"<br />
AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector"<br />
OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector"<br />
AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector"<br />
LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector"<br />
PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector"<br />
UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector"<br />
GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector"<br />
FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector"<br />
MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector"<br />
MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"<br />
</source><br />
<br />
Here all the public services (those where the viewer connects directly to the service) are served on port 8002, with internal services on 8003. So you need to make sure that the viewer can access port 8002 over TCP but you do not want to expose port 8003. Only simulators need to be able to connect to port 8003.<br />
<br />
== Connecting to a grid installation ==<br />
<br />
Your client startup line will look something like<br />
<br />
-loginuri http://mygrid.com:8002<br />
<br />
The loginuri needs to be the address to the login service. In standalone mode, this was the same address as the region simulator and the port was 9000 by default. However, in grid mode it will be the address to login service hosted on the ROBUST instance. In this case, the address will be 192.168.1.2. The port number of 8002 is the traditional one for the grid login service and is the default in Robust.ini.example.<br />
<br />
If the login is successful, you will see log lines on the ROBUST console (for the login itself) and then log lines on the region simulator console (as the login process tells the simulator to expect the avatar, tells the viewer the address of the region simulator and then when the viewer starts talking to the simulator directly).<br />
<br />
= Running multiple ROBUST service instances =<br />
<b>WARNING:<br><br />
This is a tricky issue and contrary to past allegations, opensim is not well prepared for this kind of microservices concepts.<br><br />
You can split some services by different machines, if they have no interdependencies. For example current XBakes can run anywhere<br><br />
Running several instances of same service is a invite to fail.<br><br />
Some services do assume they are the only instance, like current PresenceService, GriduserService, etc, and<br />
even do own caching to reduce load on the database engine, and that cache cannot be syncronized across instances<br><br />
Most will only service http in paralell, placing the same, and most of the load, on the same database engine. So unless that database service is a distributed system, doing proper local caching and synchronization with a master database, the gains of having multiple instances is not that large, if any if if not a total fail<br><br />
<br />
Another important thing to note is that currently several services need to read data from the configuration of other services. For example Gridservice needs to read data from its section, GridService, and sections LoginService, Hypergrid and GridInfoService. So the ini file for the robust instance running it needs to have those sections, and of course updated. <br />
</b><br><br />
<br />
<br />
If you are operating a grid, then you can run different services (e.g. asset, inventory) in different ROBUST instances, in order to spread the load. To do this, you will need to edit the ServiceConnectors parameter in the [Startup] section of Robust.ini (or [[Configuration/files/Robust/Robust.HG.ini|Robust.HG.ini]] if you're running Hypergrid). The default ServiceConnectors parameter looks something like this<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector"<br />
InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector"<br />
GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector"<br />
GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector"<br />
AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector"<br />
OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector"<br />
AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector"<br />
LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector"<br />
PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector"<br />
UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector"<br />
GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector"<br />
FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector"<br />
MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector"<br />
MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"<br />
</source><br />
<br />
Each entry has the form <br />
<br />
<port-number>/<dll>:<connector-class-name><br />
<br />
For instance, the first entry above<br />
<br />
8003/OpenSim.Server.Handlers.dll:AssetServiceConnector<br />
<br />
says to start an AssetServiceConnector (and hence an asset service) from the OpenSim.Server.Handlers.dll and to server that from port 8003.<br />
<br />
By default, Robust.exe loads a configuration file with the same name but with .ini appended instead of .exe. So Robust.exe will look for an inifile called Robust.ini. You can change this by giving the parameter on the commandline. For instance, to start Robust with HG parameters, one would use<br />
<br />
Robust.exe -inifile=Robust.HG.ini<br />
<br />
So if you wanted to run every connector apart from assets in one instance of ROBUST and the asset connector in another instance, you would have two ini files. One could remain Robust.ini and have<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector"<br />
GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector"<br />
GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector"<br />
AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector"<br />
OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector"<br />
AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector"<br />
LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector"<br />
PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector"<br />
UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector"<br />
GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector"<br />
FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector"<br />
MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector"<br />
MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"<br />
</source><br />
<br />
The other could be called Robust.Assets.ini and have<br />
<br />
<source lang="ini"><br />
[ServiceList]<br />
AssetServiceConnector = "8004/OpenSim.Server.Handlers.dll:AssetServiceConnector"<br />
</source><br />
<br />
Note that this is using port 8004 instead of port 8003. This is necessary since only one executable can use each port at a time. You will need to make sure your simulator configuration files use port 8004 for the asset service as well.<br />
<br />
You will also need to change the default network port to 8004 for this second copy of Robust.exe<br />
<br />
<pre><br />
[Network]<br />
port = 8004<br />
</pre><br />
<br />
Once you've created the two ROBUST configuration files (Robust.ini containing all services apart from asset and Robust.Assets.ini containing only the asset service), then you could start the first Robust.exe as usual.<br />
<br />
Robust.exe<br />
<br />
This will load Robust.ini, as we haven't specified a Robust.ini. Also, the logfile it will use will be Robust.log as we haven't manually specified one.<br />
<br />
The second ROBUST instance we would start with<br />
<br />
Robust.exe -inifile=Robust.Assets.ini -logfile=Robust.Assets.log<br />
<br />
The -inifile switch tells the second Robust instance to load it's configuration from Robust.Assets.ini rather than Robust.ini. The -logfile switch tells Robust.exe to use Robust.Assets.log as its logfile rather than the default Robust.log. If you don't specify this switch then you may see errors on the console about a locked log file.<br />
<br />
At this point you should have two running Robust.exe instances.<br />
<br />
If you put the ROBUST instances on different machines then don't forget to change the relevant service URIs in each simulator to match.<br />
<br />
Since OpenSimulator services are stateless (e.g. every request is unconnected with other requests as long as they affect the same persistent data where necessary), you can also load balance by starting more than one ROBUST instance with a copy of the same service (e.g. multiple asset services using the same database). Requests would be round-robined between the service copies using an HTTP reverse proxy implementation (e.g. nginx). See [[Performance#Services]] for more details.<br />
<br />
== Notes ==<br />
[http://opensimulator.org/pipermail/opensim-users/2012-October/011099.html Useful discussion about separating Robust instances]<br />
<br />
= Attaching your sim to someone else's grid =<br />
* Make sure you understand, agree and will enforce that grid Terms of Service<br />
* Follow the instructions of that grid owner!<br />
<br />
To set up the region server (i.e., <tt>OpenSim.exe</tt>) to connect to an external grid, follow the [[Configuration#Step 2: Configure an OpenSim.exe to use the ROBUST services]] instructions above.<br />
<br />
The grid will have already provided with the required services. In step 2 you will need to use the provided URLs for their services.<br />
<br />
In your bin/Regions.ini file (or other region config file) you will also need to set the grid co-ordinates to your regions provided from the grid operator. See [[Configuring Regions]] for more information.<br />
<br />
= Running an OpenSimulator standalone or grid installation with Hypergrid enabled =<br />
[[Hypergrid]] is an emerging architecture supported by OpenSimulator that allows a user with an account on one standalone or grid to visit other Hypergrid-enabled standalones or grids, and for users from those grids to visit the home grid. This does not require the two installations to share a central set of data services (assets, inventory, etc.). Please see [[Installing and Running Hypergrid]] for more details.<br />
<br />
= Further notes =<br />
<br />
== Troubleshooting ==<br />
<br />
See [[Troubleshooting]] <br />
<br />
== Running OpenSimulator in 32 bit in Windows 64bit==<br />
<br />
Windows 64Bit can run several instances of OpenSim in 32Bit mode, allowing each to use up to about 2GB of total physical ram.<br />
<br />
This is almost the total memory available on 32Bit versions of Windows, for all the programs (around 3GB)<br />
<br />
In 32Bit mode, the simulator uses less memory and may be a bit faster, so unless you have very large regions, you can ran it in 32Bit mode, just run:<br />
<br />
OpenSim32.exe instead of OpenSim.exe<br />
<br />
You can also run Robust in 32Bit mode:<br />
<br />
Robust32.exe in place of Robust.exe<br />
<br />
Linux 64Bit only runs the simulator in 64Bit mode<br />
<br />
== Increasing the stack reserve level when using OpenDynamicsEngine (old ODE and ubODE) on Linux or other Unix variants ==<br />
<br />
Increase your stack reserve level with the following command;<br />
<tt>ulimit -s 262144</tt> Or, run the opensim.sh to start up OpenSimulator.<br />
<br />
== Firewalls ==<br />
Some operation systems or distributions run their own firewall by default. If you can't access to OpenSimulator from remote client, you'll need to check their settings. See [[Firewall Settings]] for details.<br />
<br />
== Legacy Configuration Information ==<br />
These are some pages containing some legacy configuration information of unknown accuracy.<br />
<br />
[[OpenSim 0.6.6 legacy configuration information]]<br />
<br />
<br />
== Additional Optional Configuration Tasks ==<br />
<br />
=== Further configure OpenSimulator ===<br />
If you've looked through OpenSim.ini.example or any other of the config files, you'll see that there's a very large number of configurable parameters spread across multiple files. See [[Configuring Simulator Parameters]] for more details about the configuration infrastructure and how settings in identically named sections (e.g. [XEngine]) are merged by OpenSimulator on loading.<br />
<br />
=== Set up a second region to run on the same simulator ===<br />
See [[Configuring Regions]].<br />
<br />
=== Run Multiple Standalone Instances of OpenSimulator on the Same Server ===<br />
For each subsequent instance of OpenSim, change the 'http_listener_port' in [[Configuration/files/OpenSim/OpenSim.ini|OpenSim.ini]] to the value excluding 9000, and 'InternalPort' in Regions.ini to the value excluding 9000. Also, make sure your regions are using different ports, as explained in [[Configuring Regions]].<br />
<br />
=== Load region content ===<br />
You can load content onto regions by using the [[OpenSim Archives|load oar command]]. To load individual OAR files into each region, use the 'change region [regionname]' command and then 'load oar [oar-location]'.<br />
<br />
=== OpenSim.exe command line options ===<br />
OpenSim.exe has command line options which allow you to perform actions such as reading configuration files from a different directory. See [[OpenSim.exe Command Line Options]] for more details.<br />
<br />
=== Script engine ===<br />
OpenSimulator supports multiple script engines. See [[ScriptEngines]] for details. If you don't know what this means then the default script engine will be fine. In fact, recent versions of OpenSimulator only ship with one script engine, the XEngine.<br />
<br />
=== Permissions Configuration ===<br />
OpenSimulator has a quite elaborate set of permissions. See [[Permissions (Server)]] for details. By default, permissions are active on region simulators.<br />
<br />
=== Logging ===<br />
By default, OpenSimulator logs information to a file called OpenSim.log in the bin directory. See [[Logging]] for details on how to further configure this if required.<br />
<br />
=== FSAsset Service ===<br />
By default the OpenSimulator asset service will store assets in the robust database. If you expect your asset data to grow larger than 50Gb you should consider changing to the [[FSAssets Service]]. (FSAssets is a new service and is currently only available in the latest development branch)<br />
<br />
=== Set up other optional modules ===<br />
* [[IRCBridgeModule]]<br />
* [[Freeswitch Module]]<br />
* [[Offline Messaging]]<br />
* [[Enabling Groups]]<br />
* See also [[User_Documentation#Setup]]<br />
<br />
=== Configuration of Web Server and Pages ===<br />
OpenSimulator contains a web server that can serve up a variety of pages. Some which come from external files and some are generated internally.<br />
* [[External Files]]<br />
* [[Internally Generated]]<br />
<br />
= Where to go from here =<br />
<br />
* [[NAT Loopback Routers]] Router and Nat Loopback Information<br />
<br />
* [[Upgrading]] from an old OpenSimulator version to a newer one.<br />
<br />
* [[Configuring_Simulator_Parameters]] cascading configuration structure, environment variables<br />
<br />
* [[Server Commands]] for creating users and controlling the system.<br />
<br />
= References =<br />
<br />
* [http://dist.opensimulator.org/wiki/opensim-standalone.odg OpenOffice draw file for OpenSimulator standalone diagram]<br />
* [http://dist.opensimulator.org/wiki/opensim-grid-simple.odg OpenOffice draw file for OpenSimulator grid diagram]<br />
<br />
[[Category:Configuration]]</div>Aiaustinhttp://opensimulator.org/wiki/LLUDP_ClientStackLLUDP ClientStack2023-01-11T13:02:52Z<p>Aiaustin: /* Useful console commands */ debug lludp commands no longer available.</p>
<hr />
<div>{{MainPageQuicklinks}}<br />
<br />
=Introduction=<br />
<br />
These are draft notes on OpenSimulator's implementation of the LLUDP ClientStack, a chunk of code which is used to send and receive packets from viewers (clients) implementing the Linden Labs virtual environment protocol.<br />
<br />
As such, this stack handles <br />
<br />
* Setup of an inbound UDP handling stack for each region.<br />
* Handling of inbound UDP messages from a connected viewer.<br />
* Distribution of recieved UDP messages to appropriate handling code (e.g. selection handling code if a prim is selected).<br />
* Sending of outbound UDP messages to a connected viewer.<br />
* Throttling of outbound messages.<br />
* Sending and receive of ack messages, both inline within other messages and as standalone messages.<br />
* Resending of messages that are marked as reliable but for which receipt has not been acknowledged by the viewer.<br />
* Throttling of outgoing UDP messages.<br />
* Pooling of clientstack structures (e.g. classes representing messages) in order to improve efficiency and reduce memory usage.<br />
<br />
=Useful console commands=<br />
<br />
Note these console commands are no longer available in OpenSimulator. The description below is left in place for historical information.<br />
<br />
These are console commands which are useful in debugging or investigating the LLUDP protocol.<br />
<br />
* debug lludp packet [--default | --all] <level> [<avatar-first-name> <avatar-last-name>] - Turn on packet debugging. In OpenSimulator 0.7.5 and previous this command was "debug packet". "--default" applies the new logging level to all avatar that login after that point. "--all" applies it to all existing avatars as well.<br />
* debug lludp pool <on|off> - Turn object pooling within the lludp component on or off.<br />
* debug lludp start <in|out|all> - Control LLUDP packet processing.<br />
* debug lludp status - Return status of LLUDP packet processing.<br />
* debug lludp stop <in|out|all> - Stop LLUDP packet processing.<br />
<br />
=Inbound UDP=<br />
<br />
== Code paths ==<br />
<br />
===Part 1===<br />
* Inbound UDP messages are received on a region's listening UDP port (currently in OpenSimUDPBase). These are IOCP threads from the runtime as processing is done asynchronously.<br />
* On receive, the thread kicks off another asychrnous receive (which may be handled by a different IOCP thread).<br />
* Each received packet is sanity checked for length and basic malformations.<br />
* Data is inserted into a packet structure drawn from a pool.<br />
* If packet <br />
** Is UseCircuitCode (used to set up circuits with viewers) then this is handled on a separate thread.<br />
** Is CompleteAgentMovement (used to complete the insertion of an avatar into a region) then this is handled on a separate thread.<br />
** Has appended acks to a normal packet or is PacketAck then these are processed.<br />
** Is reliable then a pending ack is queued for acknowledgement to the client.<br />
** Is AgentUpdate and is not significant (same as last packet, where these are received about 10 times a second) then it is discarded.<br />
** StartPingCheck then this is handled.<br />
* All other packets are placed into a queue (packetInbox) for further processing.<br />
* The thread is released (synchronous fetch not currently operational).<br />
<br />
===Part 2===<br />
* A continuously running thread fetches the next packet at the front of the packetInbox queue.<br />
* It enters the LLClientView structure, logging details of the packet for debug purposes if required.<br />
* The packet is processed either synchronously (on this same thread) or asynchronously by queueing it as a work item to a threadpool (SmartThreadPool by default).<br />
** Some packets are handled synchronously due to issues if they are processed out of order (e.g. multi-face texture changes not occuring properly). Others may be synchronous by default where they could actually be handled asynchronously.<br />
* The long running thread loops.<br />
<br />
=Outbound UDP=<br />
<br />
== Code paths ==<br />
'''This section is very incomplete.'''<br />
* LLUDPServer.SendPacketFinal() is the penultimate method before UDP data is placed on the wire.<br />
** If the packet is not zero-coded and has room, then any waiting acks are appended.<br />
** If the packet is reliable then the server records its expectation of acks from the client.<br />
<br />
* If there are no packets waiting to be sent for a throttle and none in the outbox, then BeginFireQueueEmpty().<br />
<br />
* LLUDPClient.BeingFireQueueEmpty fires HasUpdates event is fired to determine if updates are waiting.<br />
** LLClientView.HandleHasUpdates() handles this and returns true if it has queued entity updates, entity property updates or image manager updates (UDP asset download).<br />
<br />
* If there are such updates waiting, a threadpool FireQueueEmpty() thread is triggered<br />
* This then fires the OnQueueEmpty event.<br />
** Which LLClientView handles with HandleQueueEmpty() which places a certain number of updates via OutPacket()<br />
<br />
== Throttles ==<br />
'''There is also an old informal discussion of throttles at [[Sim Throttles]] which may or may not be accurate.'''<br />
<br />
Outbound UDP is throttled per client, with an optional throttle for the entire scene.<br />
<br />
=== Settings ===<br />
<br />
If <br />
<br />
<source lang="ini"><br />
[ClientStack.LindenUDP]<br />
enable_adaptive_throttles = true<br />
</source><br />
<br />
(the current default). Then client throttles are adjusted automatically by the server. Whilst the connection is fine, rates are increased. However, if packets marked as reliable are not received in a timely manner, then the throttle is reduced until no more packets are lost. The throttle is then gradually increased again until either the throttle requested by the client is reached or more packets are lost.<br />
<br />
If<br />
<br />
<source lang="ini"><br />
[ClientStack.LindenUDP]<br />
enable_adaptive_throttles = false<br />
</source><br />
<br />
Then throttles are set by the client. <br />
<br />
In dependent of adaptive or client throttle settings, throttle rates may still be restricted on the server side by setting either or both of<br />
<br />
<source lang="ini"><br />
[ClientStack.LindenUDP]<br />
client_throttle_max_bps = <some-client-limit><br />
scene_throttle_max_bps = <some-entire-scene-limit><br />
</source><br />
<br />
The client_throttle_max_bps setting will enforce a maximum limit for each client connection. The scene_throttle_max_bps setting will set a maximum output limit for all connections to a scene. So for this latter limit, every extra connection will reduce the bandwidth for everybody as a fixed limit is shared between more clients. This includes child connections (used for clients with avatars in neighbouring regions to see activity in this region).<br />
<br />
Neither of these limits are set by default.<br />
<br />
=Other references=<br />
<br />
* [[Sim Throttles]] contains very old information on the implementation of throttles. This has likely changed considerably but could still be useful.</div>Aiaustinhttp://opensimulator.org/wiki/YEngineYEngine2022-08-21T10:21:35Z<p>Aiaustin: Improve</p>
<hr />
<div>{{MainPageQuicklinks}}<br />
<br />
<br />
=Summary=<br />
<br />
This is a fully functional alternative to [[XEngine]] for LSL/OSSL scripts, featuring<br />
<br />
* Preemptive Multitasking<br />
* Improved script syntax<br />
* Expressions evaluation order closer to original LSL specification<br />
* Memory heap and stack use control per script<br />
* Optional new language extensions<br />
<br />
YEngine does direct translation from script language to .net IL code, this makes it compile faster than XEngine.<br />
XEngine translates script language to C#, and then uses .net compiler to generate IL, this is naturally slower.<br />
While XEngine could compile scripts in other languages like C#, YEngine only supports LSL.<br />
Due to security issues, those other languages could not be actually used, except on very special and restricted cases.<br />
<br />
Once a script code is loaded on XEngine, the memory used by it can not be recovered whne the script is deleted, unless a .net feature called AppDomains are used<br><br />
But AppDomains are very heavy, use a lot more memory and worse, have a huge negative impact on scripts performance<br><br />
On calls crossing domains ( ie the script and main framework) all parameters and return values are serialized and deserialized by sender and receiver domains.<br><br />
<br />
Yengine loads scripts code in a diferent way, most their memory is recovered on delete without the need for those AppDomains.<br />
<br />
= Preemptive Multitasking =<br />
YEngine executes script events using preemptive multitasking.<br><br />
This means that, at certain control points or when told, a script may release execution, being placed on a queue waiting for its turn to execute again.<br><br />
The released execution thread goes on processing other scripts.<br><br />
<br />
When a new event started execution, it will be allowed to run for about 60ms (subject to change) until releasing execution.<br />
When it is is turn to execute again it will be allowed to run for another 30ms(subject to change). This will be repeated until all the event code is executed<br><br />
Engine will give more priority to short fast events.<br />
<br />
The script can also be placed in a sleep state until a sleep time lapses.<br><br />
This, for example, solves one of XEngine worse problems: llSleep() and other internal script sleeps. On YEngine, this just releases execution and waits in a queue, until the sleep time expires. On XEngine an "expensive" execution thread was placed in Sleep mode, so not available to do anything.<br />
<br />
= Improved script syntax =<br />
YEngine follows more closely the LSL script syntax and execution order.<br />
statements like <br />
if(oneKey)<br />
...<br />
<br />
should now work.<br />
<br />
llSomething; // missing()<br />
break; // but see new extensions<br />
<br />
are now errors<br />
<br />
= Expressions evaluation order =<br />
<br />
Complex statements execution order is closer to LSL specification than XEngine execution order.<br />
<br />
for example, with i = 1;<br />
((i == 2) && (i++ == 1))<br />
<br />
will evaluate to true as by LSL spec, while on XEngine it will evaluate to false, because it will do (i == 2) first<br />
<br />
Even if not useful in this case, <b>always use parentheses (...) to enforce the order you need (and do this in any language)!</b><br />
<br />
= New language extensions =<br />
<div style="background-color:#FFA500; padding:10px; padding-bottom:5px; border: 1px #FF544F solid"><br />
This information is relative to version Yeti 0.9.2.0 Dev, with older version use just only normal LSL/OSSL<br />
</div><br><br />
<br />
<div style="background-color:#FFA0A0; padding:10px; padding-bottom:5px; border: 1px #FF544F solid"><br />
Scripts using these features will only work on a compatible YEngine version. They will not compile or run on XEngine or older versions of YEngine.<br />
</div><br><br />
<br />
If the second line of a script is (currently first line needs to be present, empty or with script engine selection)<br />
yoptions;<br />
<br />
The following Yengine specific language extensions are activated<br />
<br />
* [[YENGswitch | switch]]<br />
* [[YENGbreak | break]]<br />
* [[YENGcontinue | continue]]<br />
* [[YENGtry | try catch and finally]]<br />
* [[YENGconstant | constant]]<br />
* [[YENGShortcircuit | short circuit AND and OR]]<br />
<br />
Scripts using these features will not compile or run on XEngine.<br />
<br />
= Memory heap and stack use control =<br />
YEngine keeps control of the memory a script uses.<br><br />
There are two types of memory:<br><br />
* Stack holds function arguments and simple local variables.<br><br />
* Heap holds global variables and complex variables like lists or strings, even if they are local to a method/event.<br><br />
<br />
The maximum memory a script can use can be configured in the OpenSim.ini section [YEngine]<br />
<br />
; maximum stack a script can use in KB<br />
;ScriptStackSize = 2048<br />
<br />
; maximum heap memory a script can use in KB<br />
;ScriptHeapSize = 1024<br />
<br />
You may need to increase these values<br />
<br />
= Activation =<br />
OpenSimulator default configuration selects XEngine. To change to YEngine you need to change OpenSim.ini:<br><br />
[Startup] section:<br />
<br />
DefaultScriptEngine= "YEngine"<br />
<br />
[YEngine] section:<br />
<br />
Enable = true<br />
<br />
[XEngine] section:<br />
<br />
Enable = false<br />
<br />
<br />
Note: in theory, OpenSimulator could run several engines at the same time, but we should not do that with X and Y engines.<br />
<br />
= Configuration =<br />
Please see file OpenSimDefaults.ini, section [YEngine] for details.<br><br />
As in all case, if you need to change something, copy respective lines to similar location on file OpenSim.ini, and change there<br />
<br />
= Origins =<br />
YEngine is a modified derivative of XMREngine.<br><br />
XMREngine was developed by teams of DreamNation and Avination grids, based on early work by Meta7.<br><br />
It is still in use by DreamNation.<br><br />
<br />
http://wiki.dreamnation.net/index.php/XMREngine_Script_Engine<br />
<br />
A lot of information about XMREngine no longer applies to YEngine. Some features may still work, but may be removed or changed.<br />
<br />
[[Category:Scripts]]</div>Aiaustinhttp://opensimulator.org/wiki/Talk:InventoryTalk:Inventory2022-05-14T21:38:33Z<p>Aiaustin: Move Ubit's RLV note to discussion page</p>
<hr />
<div>Ubit notes, 14-May-2022<br />
<br />
Inventory was supposed to be somewhat flexible, closer to what we see in most filesystems. Sadly viewers drift into game like inventories, expecting more rigid structure and rigid use rather that just optional "preferred" structure. While some of thiese changes are acceptable, others are not, since they will break on older opensimulator versions and worse will break with inventory backup files<br />
<br />
Recent viewers are incorporating code to try enforce such game like restrictions. Viewers for opensimulator must stay compatible and fully functional with inventories that do, for example, have folders with same name and or type, anywhere on the inventory tree. Viewers should validate the integrity of the folders tree, namely parent/child relations, and verify lost Llnks.<br />
<br />
RLV is not an officially supported feature, it must use normal inventory structure not dependent on a particular simulator response or operational details.</div>Aiaustinhttp://opensimulator.org/wiki/InventoryInventory2022-05-14T21:35:50Z<p>Aiaustin: /* OpenSim Inventory Folder Types */ Move RLV warning to discussion page</p>
<hr />
<div>From 2022 some viewers have code designed to validate whether the inventory structure is properly set up and may have facilities to warn users if there are problems with their inventory structure. <br />
<br />
== OpenSim Inventory Folder Types ==<br />
<br />
OpenSim has been permissive about inventory folder duplication. However, Opensimulator until 2015 used folder types that where different from the ones expected by viewers, so Region and Grid owners should pay attention to some cases where viewer side restrictions are being validated:<br />
<br />
* My Inventory or Inventory: should be type 8 and each user must have only one. (type 9 was used on older versions)<br />
* My Suitcase or Suitcase: is optional. If present, should be type 100 and each user must have only one. (type 8 or 9 was used on older versions)<br />
* Trash: should be type 14<br />
* Lost and Found: should be type 16<br />
* Current Outfit: must be type 46<br />
<br />
{{Warning|IAR load DOES create duplicate folders unless the --merge option is used}}<br />
<br />
Viewers will look for specific folders, mostly only on creating a new item, So if they are duplicated, it just may not be clear the folder they choose. Users should be advised to only have duplications while doing merge of inventories, etc. Folder types in OpenSim have still no other use than show type specific icons and to allow use of inventory filters.<br />
<br />
Also see the informative Wiki page on [[Inventoryfolders]].<br />
<br />
== External Discussions on Inventory Issues ==<br />
<br />
* http://opensimulator.org/mantis/view.php?id=8997 - OpenSimulator Mantis 8997 - Inventory Validation Checks in Recent Viewers Causes Issues for Inventory Folders with wrong type numbers.<br />
* https://jira.firestormviewer.org/browse/FIRE-31634 - Firestorm JIRA FIRE-31634 Opensim - Inventory doesn't load on some grids.<br />
<br />
== Check and Correct Inventory Script (User Contribution) ==<br />
<br />
See also [[Check_inventory_script]] for advice and a PHP script which can help fix some inventory type issues.</div>Aiaustinhttp://opensimulator.org/wiki/InventoryInventory2022-05-14T21:34:11Z<p>Aiaustin: /* OpenSim Inventory Folder Types */ -- merge</p>
<hr />
<div>From 2022 some viewers have code designed to validate whether the inventory structure is properly set up and may have facilities to warn users if there are problems with their inventory structure. <br />
<br />
== OpenSim Inventory Folder Types ==<br />
<br />
OpenSim has been permissive about inventory folder duplication. However, Opensimulator until 2015 used folder types that where different from the ones expected by viewers, so Region and Grid owners should pay attention to some cases where viewer side restrictions are being validated:<br />
<br />
* My Inventory or Inventory: should be type 8 and each user must have only one. (type 9 was used on older versions)<br />
* My Suitcase or Suitcase: is optional. If present, should be type 100 and each user must have only one. (type 8 or 9 was used on older versions)<br />
* Trash: should be type 14<br />
* Lost and Found: should be type 16<br />
* Current Outfit: must be type 46<br />
<br />
{{Warning|IAR load DOES create duplicate folders unless the --merge option is used}}<br />
<br />
Viewers will look for specific folders, mostly only on creating a new item, So if they are duplicated, it just may not be clear the folder they choose. Users should be advised to only have duplications while doing merge of inventories, etc. Folder types in OpenSim have still no other use than show type specific icons and to allow use of inventory filters.<br />
<br />
{{Warning|RLV is not an officially supported feature on itself, it must use normal inventory structure not dependent on a peculiar simulator response or operation details.}}<br />
<br />
Also see the informative Wiki page on [[Inventoryfolders]].<br />
<br />
== External Discussions on Inventory Issues ==<br />
<br />
* http://opensimulator.org/mantis/view.php?id=8997 - OpenSimulator Mantis 8997 - Inventory Validation Checks in Recent Viewers Causes Issues for Inventory Folders with wrong type numbers.<br />
* https://jira.firestormviewer.org/browse/FIRE-31634 - Firestorm JIRA FIRE-31634 Opensim - Inventory doesn't load on some grids.<br />
<br />
== Check and Correct Inventory Script (User Contribution) ==<br />
<br />
See also [[Check_inventory_script]] for advice and a PHP script which can help fix some inventory type issues.</div>Aiaustinhttp://opensimulator.org/wiki/Talk:InventoryTalk:Inventory2022-05-14T20:56:31Z<p>Aiaustin: typo</p>
<hr />
<div>Ubit notes, 14-May-2022<br />
<br />
Inventory was supposed to be somewhat flexible, closer to what we see in most filesystems. Sadly viewers drift into game like inventories, expecting more rigid structure and rigid use rather that just optional "preferred" structure. While some of thiese changes are acceptable, others are not, since they will break on older opensimulator versions and worse will break with inventory backup files<br />
<br />
Recent viewers are incorporating code to try enforce such game like restrictions. Viewers for opensimulator must stay compatible and fully functional with inventories that do, for example, have folders with same name and or type, anywhere on the inventory tree. Viewers should validate the integrity of the folders tree, namely parent/child relations, and verify lost Llnks.</div>Aiaustinhttp://opensimulator.org/wiki/Talk:InventoryTalk:Inventory2022-05-14T20:56:11Z<p>Aiaustin: Add para break as in original Ubit notes</p>
<hr />
<div>Ubit notes, 14-May-2022<br />
<br />
Inventory was supposed to be somewhat flexible, closer to what we see in most filesystems. Sadly viewers drift into game like inventories, expecting more rigid structure and rigid use rather that just optional "preferred" structure. While some of thiese changes are acceptable, others are not, since they will break on older opensimulator versions and worse will break with inventory backup files<br />
<br />
Recent viewers are incorporatinh code to try enforce such game like restrictions. Viewers for opensimulator must stay compatible and fully functional with inventories that do, for example, have folders with same name and or type, anywhere on the inventory tree. Viewers should validate the integrity of the folders tree, namely parent/child relations, and verify lost Llnks.</div>Aiaustinhttp://opensimulator.org/wiki/Talk:InventoryTalk:Inventory2022-05-14T20:30:09Z<p>Aiaustin: Summary if Ubit's duscussion remived from the main advice page fir Inventiry.</p>
<hr />
<div>Ubit notes, 14-May-2022<br />
<br />
Inventory was supposed to be somewhat flexible, closer to what we see in most filesystems. Sadly viewers drift into game like inventories, expecting more rigid structure and rigid use rather that just optional "preferred" structure. While some of thiese changes are acceptable, others are not, since they will break on older opensimulator versions and worse will break with inventory backup files. Recent viewers are merging code to try enforce such game like restrictions. Viewers for opensimulator must stay compatible and fully functional with inventories that do, for example, have folders with same name and or type, anywhere on the inventory tree. Viewers should validate the integrity of the folders tree, namely parent/child relations, and verify lost Llnks.</div>Aiaustinhttp://opensimulator.org/wiki/InventoryInventory2022-05-14T20:17:53Z<p>Aiaustin: /* Check Inventory Script (User Contribution) */</p>
<hr />
<div>From 2022 some viewers have code designed to validate whether the inventory structure is properly set up and may have facilities to warn users if there are problems with their inventory structure. <br />
<br />
== OpenSim Inventory Folder Types ==<br />
<br />
OpenSim has been permissive about inventory folder duplication. However, Opensimulator until 2015 used folder types that where different from the ones expected by viewers, so Region and Grid owners should pay attention to some cases where viewer side restrictions are being validated:<br />
<br />
* My Inventory or Inventory: should be type 8 and each user must have only one. (type 9 was used on older versions)<br />
* My Suitcase or Suitcase: is optional. If present, should be type 100 and each user must have only one. (type 8 or 9 was used on older versions)<br />
* Trash: should be type 14<br />
* Lost and Found: should be type 16<br />
* Current Outfit: must be type 46<br />
<br />
{{Warning|IAR load DOES duplicate folders unless the merge option is used}}<br />
<br />
Viewers will look for specific folders, mostly only on creating a new item, So if they are duplicated, it just may not be clear the folder they choose. Users should be advised to only have duplications while doing merge of inventories, etc. Folder types in OpenSim have still no other use than show type specific icons and to allow use of inventory filters.<br />
<br />
{{Warning|RLV is not an officially supported feature on itself, it must use normal inventory structure not dependent on a peculiar simulator response or operation details.}}<br />
<br />
Also see the informative Wiki page on [[Inventoryfolders]].<br />
<br />
== External Discussions on Inventory Issues ==<br />
<br />
* http://opensimulator.org/mantis/view.php?id=8997 - OpenSimulator Mantis 8997 - Inventory Validation Checks in Recent Viewers Causes Issues for Inventory Folders with wrong type numbers.<br />
* https://jira.firestormviewer.org/browse/FIRE-31634 - Firestorm JIRA FIRE-31634 Opensim - Inventory doesn't load on some grids.<br />
<br />
== Check and Correct Inventory Script (User Contribution) ==<br />
<br />
See also [[Check_inventory_script]] for advice and a PHP script which can help fix some inventory type issues.</div>Aiaustinhttp://opensimulator.org/wiki/InventoryInventory2022-05-14T20:17:26Z<p>Aiaustin: /* OpenSim Inventory Folder Types */</p>
<hr />
<div>From 2022 some viewers have code designed to validate whether the inventory structure is properly set up and may have facilities to warn users if there are problems with their inventory structure. <br />
<br />
== OpenSim Inventory Folder Types ==<br />
<br />
OpenSim has been permissive about inventory folder duplication. However, Opensimulator until 2015 used folder types that where different from the ones expected by viewers, so Region and Grid owners should pay attention to some cases where viewer side restrictions are being validated:<br />
<br />
* My Inventory or Inventory: should be type 8 and each user must have only one. (type 9 was used on older versions)<br />
* My Suitcase or Suitcase: is optional. If present, should be type 100 and each user must have only one. (type 8 or 9 was used on older versions)<br />
* Trash: should be type 14<br />
* Lost and Found: should be type 16<br />
* Current Outfit: must be type 46<br />
<br />
{{Warning|IAR load DOES duplicate folders unless the merge option is used}}<br />
<br />
Viewers will look for specific folders, mostly only on creating a new item, So if they are duplicated, it just may not be clear the folder they choose. Users should be advised to only have duplications while doing merge of inventories, etc. Folder types in OpenSim have still no other use than show type specific icons and to allow use of inventory filters.<br />
<br />
{{Warning|RLV is not an officially supported feature on itself, it must use normal inventory structure not dependent on a peculiar simulator response or operation details.}}<br />
<br />
Also see the informative Wiki page on [[Inventoryfolders]].<br />
<br />
== External Discussions on Inventory Issues ==<br />
<br />
* http://opensimulator.org/mantis/view.php?id=8997 - OpenSimulator Mantis 8997 - Inventory Validation Checks in Recent Viewers Causes Issues for Inventory Folders with wrong type numbers.<br />
* https://jira.firestormviewer.org/browse/FIRE-31634 - Firestorm JIRA FIRE-31634 Opensim - Inventory doesn't load on some grids.<br />
<br />
== Check Inventory Script (User Contribution) ==<br />
<br />
See also [[Check_inventory_script]] for advice and a PHP script which can help fix some inventory type issues.</div>Aiaustinhttp://opensimulator.org/wiki/InventoryInventory2022-05-14T20:10:52Z<p>Aiaustin: Typo</p>
<hr />
<div>From 2022 some viewers have code designed to validate whether the inventory structure is properly set up and may have facilities to warn users if there are problems with their inventory structure. <br />
<br />
== OpenSim Inventory Folder Types ==<br />
<br />
OpenSim has been permissive about inventory folder duplication. However, Opensimulator until 2015 used folder types that where different from the ones expected by viewers, so Region and Grid owners should pay attention to some cases where viewer side restrictions are being validated:<br />
<br />
* My Inventory or Inventory: should be type 8 and each user must have only one. (type 9 was used on older versions)<br />
* My Suitcase or Suitcase: is optional. If present, should be type 100 and each user must have only one. (type 8 or 9 was used on older versions)<br />
* Trash: should be type 14<br />
* Lost and Found: should be type 16<br />
* Current Outfit: must be type 46<br />
<br />
{{Warning|IAR load DOES duplicate folders unless the merge option is used, and will go on doing so}}<br />
<br />
Viewers will look for specific folders, mostly only on creating a new item, So if they are duplicated, it just may not be clear the folder they choose. Users should be advised to only have duplications while doing merge of inventories, etc. Folder types in OpenSim have still no other use than show type specific icons and to allow use of inventory filters.<br />
<br />
{{Warning|RLV is not an officially supported feature on itself, it must use normal inventory structure not dependent on a peculiar simulator response or operation details.}}<br />
<br />
Also see the informative Wiki page on [[Inventoryfolders]].<br />
<br />
== External Discussions on Inventory Issues ==<br />
<br />
* http://opensimulator.org/mantis/view.php?id=8997 - OpenSimulator Mantis 8997 - Inventory Validation Checks in Recent Viewers Causes Issues for Inventory Folders with wrong type numbers.<br />
* https://jira.firestormviewer.org/browse/FIRE-31634 - Firestorm JIRA FIRE-31634 Opensim - Inventory doesn't load on some grids.<br />
<br />
== Check Inventory Script (User Contribution) ==<br />
<br />
See also [[Check_inventory_script]] for advice and a PHP script which can help fix some inventory type issues.</div>Aiaustinhttp://opensimulator.org/wiki/InventoryInventory2022-05-14T20:08:02Z<p>Aiaustin: Type</p>
<hr />
<div>From 2022 some viewers have code designed to validate whether the inventory structure is properly set up and may have facilities to warn users if there are problems with their inventory.<br />
<br />
However, Opensimulator until 2015 used folder types that where different from the ones expected by viewers, so Region and Grid owners should pay attention to some cases where viewer side restrictions are being validated:<br />
* My Inventory or Inventory: should be type 8 and each user must have only one. (type 9 was used on older versions)<br />
* My Suitcase or Suitcase: is optional. If present, should be type 100 and each user must have only one. (type 8 or 9 was used on older versions)<br />
* Trash: should be type 14<br />
* Lost and Found: should be type 16<br />
* Current Outfit: must be type 46<br />
<br />
{{Warning|IAR load DOES duplicate folders unless the merge option is used, and will go on doing so}}<br />
<br />
Viewers will look for specific folders, mostly only on creating a new item, So if they are duplicated, it just may not be clear the folder they choose. Users should be advised to only have duplications while doing merge of inventories, etc. Folder types in OpenSim have still no other use than show type specific icons and to allow use of inventory filters.<br />
<br />
{{Warning|RLV is not an officially supported feature on itself, it must use normal inventory structure not dependent on a peculiar simulator response or operation details.}}<br />
<br />
Also see the informative Wiki page on [[Inventoryfolders]].<br />
<br />
== External Discussions on Inventory Issues ==<br />
<br />
* http://opensimulator.org/mantis/view.php?id=8997 - OpenSimulator Mantis 8997 - Inventory Validation Checks in Recent Viewers Causes Issues for Inventory Folders with wrong type numbers.<br />
* https://jira.firestormviewer.org/browse/FIRE-31634 - Firestorm JIRA FIRE-31634 Opensim - Inventory doesn't load on some grids.<br />
<br />
== Check Inventory Script (User Contribution) ==<br />
<br />
See also [[Check_inventory_script]] for advice and a PHP script which can help fix some inventory type issues.</div>Aiaustinhttp://opensimulator.org/wiki/InventoryInventory2022-05-14T20:06:57Z<p>Aiaustin: Attempt to "improve" the text a little :-)</p>
<hr />
<div>From 2022 viewers have code designed to validate whether the inventory structure is properly set up and have facilities to warn users if there are problems with their inventory.<br />
<br />
However, Opensimulator until 2015 used folder types that where different from the ones expected by viewers, so Region and Grid owners should pay attention to some cases where viewer side restrictions are being validated:<br />
* My Inventory or Inventory: should be type 8 and each user must have only one. (type 9 was used on older versions)<br />
* My Suitcase or Suitcase: is optional. If present, should be type 100 and each user must have only one. (type 8 or 9 was used on older versions)<br />
* Trash: should be type 14<br />
* Lost and Found: should be type 16<br />
* Current Outfit: must be type 46<br />
<br />
{{Warning|IAR load DOES duplicate folders unless the merge option is used, and will go on doing so}}<br />
<br />
Viewers will look for specific folders, mostly only on creating a new item, So if they are duplicated, it just may not be clear the folder they choose. Users should be advised to only have duplications while doing merge of inventories, etc. Folder types in OpenSim have still no other use than show type specific icons and to allow use of inventory filters.<br />
<br />
{{Warning|RLV is not an officially supported feature on itself, it must use normal inventory structure not dependent on a peculiar simulator response or operation details.}}<br />
<br />
Also see the informative Wiki page on [[Inventoryfolders]].<br />
<br />
== External Discussions on Inventory Issues ==<br />
<br />
* http://opensimulator.org/mantis/view.php?id=8997 - OpenSimulator Mantis 8997 - Inventory Validation Checks in Recent Viewers Causes Issues for Inventory Folders with wrong type numbers.<br />
* https://jira.firestormviewer.org/browse/FIRE-31634 - Firestorm JIRA FIRE-31634 Opensim - Inventory doesn't load on some grids.<br />
<br />
== Check Inventory Script (User Contribution) ==<br />
<br />
See also [[Check_inventory_script]] for advice and a PHP script which can help fix some inventory type issues.</div>Aiaustinhttp://opensimulator.org/wiki/InventoryInventory2022-05-14T14:37:48Z<p>Aiaustin: /* Valid Folder Structure and Type Numbers */ Comment on check needed removed.</p>
<hr />
<div>Stub page for inventory issues.<br />
<br />
Prior to 2022 viewers accepted any form of inventory outside of completely malformed setups. Some viewers now have code designed to validate whether the inventory structure is properly set up. Earlier versions of OpenSim prior to 2015 have some inventory structure and folder types that can cause inventory to fail to load or can cause login to be blocked entirely. Loading some earlier saved Inventory Archive (IAR) files into any version of OpenSim can also cause the inventory to become invalid.<br />
<br />
== Valid Folder Structure and Type Numbers ==<br />
<br />
* Top Level "Inventory" or "My Inventory" folder - type 8 (earlier versions of OpenSim may have allocated type 9 to this folder)<br />
* My Suitcase - type 100 (earlier versions of OpenSim may have allocated type 8 or 9 to this folder)<br />
<br />
See also wiki page on [[Inventoryfolders]].<br />
<br />
== Issue List ==<br />
<br />
* http://opensimulator.org/mantis/view.php?id=8997 - OpenSimulator Mantis 8997 - Inventory Validation Checks in Recent Viewers Causes Issues for Inventory Folders with wrong type numbers.<br />
* https://jira.firestormviewer.org/browse/FIRE-31634 - Firestorm JIRA FIRE-31634 Opensim - Inventory doesn't load on some grids.<br />
<br />
== Check Inventory Script ==<br />
<br />
See also [[Check_inventory_script]] for advice and a PHP script which can help fix some inventory type issues.</div>Aiaustinhttp://opensimulator.org/wiki/InventoryInventory2022-05-14T14:14:32Z<p>Aiaustin: /* Valid Folder Structure and Type Numbers */ y Suitcase subfolders</p>
<hr />
<div>Stub page for inventory issues.<br />
<br />
Prior to 2022 viewers accepted any form of inventory outside of completely malformed setups. Some viewers now have code designed to validate whether the inventory structure is properly set up. Earlier versions of OpenSim prior to 2015 have some inventory structure and folder types that can cause inventory to fail to load or can cause login to be blocked entirely. Loading some earlier saved Inventory Archive (IAR) files into any version of OpenSim can also cause the inventory to become invalid.<br />
<br />
== Valid Folder Structure and Type Numbers ==<br />
<br />
THIS TO BE CHECKED.<br />
<br />
* Top Level "Inventory" or "My Inventory" folder - type 8 (earlier versions of OpenSim may have allocated type 9 to this folder)<br />
* My Suitcase - type 100 (earlier versions of OpenSim may have allocated type 8 or 9 to this folder)<br />
<br />
See also wiki page on [[Inventoryfolders]].<br />
<br />
== Issue List ==<br />
<br />
* http://opensimulator.org/mantis/view.php?id=8997 - OpenSimulator Mantis 8997 - Inventory Validation Checks in Recent Viewers Causes Issues for Inventory Folders with wrong type numbers.<br />
* https://jira.firestormviewer.org/browse/FIRE-31634 - Firestorm JIRA FIRE-31634 Opensim - Inventory doesn't load on some grids.<br />
<br />
== Check Inventory Script ==<br />
<br />
See also [[Check_inventory_script]] for advice and a PHP script which can help fix some inventory type issues.</div>Aiaustinhttp://opensimulator.org/wiki/InventoryInventory2022-05-14T11:41:12Z<p>Aiaustin: Typo</p>
<hr />
<div>Stub page for inventory issues.<br />
<br />
Prior to 2022 viewers accepted any form of inventory outside of completely malformed setups. Some viewers now have code designed to validate whether the inventory structure is properly set up. Earlier versions of OpenSim prior to 2015 have some inventory structure and folder types that can cause inventory to fail to load or can cause login to be blocked entirely. Loading some earlier saved Inventory Archive (IAR) files into any version of OpenSim can also cause the inventory to become invalid.<br />
<br />
== Valid Folder Structure and Type Numbers ==<br />
<br />
THIS TO BE CHECKED.<br />
<br />
* Top Level "Inventory" or "My Inventory" folder - type 8 (earlier versions of OpenSim may have allocated type 9 to this folder)<br />
* My Suitcase - type 100 (earlier versions of OpenSim may have allocated type 8 or 9 to this folder)<br />
* Subfolders of My Suitcase should be given type -1 (TBC???)<br />
<br />
See also wiki page on [[Inventoryfolders]].<br />
<br />
== Issue List ==<br />
<br />
* http://opensimulator.org/mantis/view.php?id=8997 - OpenSimulator Mantis 8997 - Inventory Validation Checks in Recent Viewers Causes Issues for Inventory Folders with wrong type numbers.<br />
* https://jira.firestormviewer.org/browse/FIRE-31634 - Firestorm JIRA FIRE-31634 Opensim - Inventory doesn't load on some grids.<br />
<br />
== Check Inventory Script ==<br />
<br />
See also [[Check_inventory_script]] for advice and a PHP script which can help fix some inventory type issues.</div>Aiaustinhttp://opensimulator.org/wiki/InventoryInventory2022-05-14T10:31:53Z<p>Aiaustin: Mention IAR issue.</p>
<hr />
<div>Stub page for inventory issues.<br />
<br />
Prior to 2022 viewers accepted any form of inventory outside of completely malformed setups. Some viewers now have code designed to validate whether the inventory structure is properly setup. Earlier versions of OpenSim prior to 2015 have some inventory structure and folder types that can cause inventory to fail to load or can cause login to be blocked entirely. Loading some earlier saved Inventory Archive (IAR) files into any version of OpenSim can also cause the inventory to become invalid.<br />
<br />
== Valid Folder Structure and Type Numbers ==<br />
<br />
THIS TO BE CHECKED.<br />
<br />
* Top Level "Inventory" or "My Inventory" folder - type 8 (earlier versions of OpenSim may have allocated type 9 to this folder)<br />
* My Suitcase - type 100 (earlier versions of OpenSim may have allocated type 8 or 9 to this folder)<br />
* Subfolders of My Suitcase should be given type -1 (TBC???)<br />
<br />
See also wiki page on [[Inventoryfolders]].<br />
<br />
== Issue List ==<br />
<br />
* http://opensimulator.org/mantis/view.php?id=8997 - OpenSimulator Mantis 8997 - Inventory Validation Checks in Recent Viewers Causes Issues for Inventory Folders with wrong type numbers.<br />
* https://jira.firestormviewer.org/browse/FIRE-31634 - Firestorm JIRA FIRE-31634 Opensim - Inventory doesn't load on some grids.<br />
<br />
== Check Inventory Script ==<br />
<br />
See also [[Check_inventory_script]] for advice and a PHP script which can help fix some inventory type issues.</div>Aiaustinhttp://opensimulator.org/wiki/InventoryInventory2022-05-14T10:28:56Z<p>Aiaustin: Added link to Inventoryfolders wiki page.</p>
<hr />
<div>Stub page for inventory issues.<br />
<br />
Prior to 2022 viewers accepted any form of inventory outside of completely malformed setups. Some viewers now have code designed to validate whether the inventory structure is properly setup. Earlier versions of OpenSim prior to 2015 have some inventory structure and folder types that can cause inventory to fail to load or can cause login to be blocked entirely.<br />
<br />
== Valid Folder Structure and Type Numbers ==<br />
<br />
THIS TO BE CHECKED.<br />
<br />
* Top Level "Inventory" or "My Inventory" folder - type 8 (earlier versions of OpenSim may have allocated type 9 to this folder)<br />
* My Suitcase - type 100 (earlier versions of OpenSim may have allocated type 8 or 9 to this folder)<br />
* Subfolders of My Suitcase should be given type -1 (TBC???)<br />
<br />
See also wiki page on [[Inventoryfolders]].<br />
<br />
== Issue List ==<br />
<br />
* http://opensimulator.org/mantis/view.php?id=8997 - OpenSimulator Mantis 8997 - Inventory Validation Checks in Recent Viewers Causes Issues for Inventory Folders with wrong type numbers.<br />
* https://jira.firestormviewer.org/browse/FIRE-31634 - Firestorm JIRA FIRE-31634 Opensim - Inventory doesn't load on some grids.<br />
<br />
== Check Inventory Script ==<br />
<br />
See also [[Check_inventory_script]] for advice and a PHP script which can help fix some inventory type issues.</div>Aiaustinhttp://opensimulator.org/wiki/InventoryInventory2022-05-14T10:25:46Z<p>Aiaustin: Layout Only</p>
<hr />
<div>Stub page for inventory issues.<br />
<br />
Prior to 2022 viewers accepted any form of inventory outside of completely malformed setups. Some viewers now have code designed to validate whether the inventory structure is properly setup. Earlier versions of OpenSim prior to 2015 have some inventory structure and folder types that can cause inventory to fail to load or can cause login to be blocked entirely.<br />
<br />
== Valid Folder Structure and Type Numbers ==<br />
<br />
THIS TO BE CHECKED.<br />
<br />
* Top Level "Inventory" or "My Inventory" folder - type 8 (earlier versions of OpenSim may have allocated type 9 to this folder)<br />
* My Suitcase - type 100 (earlier versions of OpenSim may have allocated type 8 or 9 to this folder)<br />
* Subfolders of My Suitcase should be given type -1 (TBC???)<br />
<br />
== Issue List ==<br />
<br />
* http://opensimulator.org/mantis/view.php?id=8997 - OpenSimulator Mantis 8997 - Inventory Validation Checks in Recent Viewers Causes Issues for Inventory Folders with wrong type numbers.<br />
* https://jira.firestormviewer.org/browse/FIRE-31634 - Firestorm JIRA FIRE-31634 Opensim - Inventory doesn't load on some grids.<br />
<br />
== Check Inventory Script ==<br />
<br />
See also [[Check_inventory_script]] for advice and a PHP script which can help fix some inventory type issues.</div>Aiaustinhttp://opensimulator.org/wiki/InventoryInventory2022-05-14T10:25:02Z<p>Aiaustin: Correct type numbers listed - to be checked.</p>
<hr />
<div>Stub page for inventory issues.<br />
<br />
Prior to 2022 viewers accepted any form of inventory outside of completely malformed setups. Some viewers now have code designed to validate whether the inventory structure is properly setup. Earlier versions of OpenSim prior to 2015 have some inventory structure and folder types that can cause inventory to fail to load or can cause login to be blocked entirely.<br />
<br />
= Valid Folder Structure and Type Numbers =<br />
<br />
THIS TO BE CHECKED.<br />
<br />
* Top Level "Inventory" or "My Inventory" folder - type 8 (earlier versions of OpenSim may have allocated type 9 to this folder)<br />
* My Suitcase - type 100 (earlier versions of OpenSim may have allocated type 8 or 9 to this folder)<br />
* Subfolders of My Suitcase should be given type -1 (TBC???)<br />
<br />
= Issue List =<br />
<br />
* http://opensimulator.org/mantis/view.php?id=8997 - OpenSimulator Mantis 8997 - Inventory Validation Checks in Recent Viewers Causes Issues for Inventory Folders with wrong type numbers.<br />
* https://jira.firestormviewer.org/browse/FIRE-31634 - Firestorm JIRA FIRE-31634 Opensim - Inventory doesn't load on some grids.<br />
<br />
= Check Inventory Script =<br />
<br />
See also [[Check_inventory_script]] for advice and a PHP script which can help fix some inventory type issues.</div>Aiaustinhttp://opensimulator.org/wiki/InventoryInventory2022-05-14T08:51:33Z<p>Aiaustin: Layout only</p>
<hr />
<div>Stub page for inventory issues.<br />
<br />
Prior to 2022 viewers accepted any form of inventory outside of completely malformed setups. Some viewers now have code designed to validate whether the inventory structure is properly setup. Earlier versions of OpenSim prior to 2015 have some inventory structure and folder types that can cause inventory to fail to load or can cause login to be blocked entirely.<br />
<br />
See https://jira.firestormviewer.org/browse/FIRE-31634 - Firestorm JIRA FIRE-31634 Opensim - Inventory doesn't load on some grids.<br />
<br />
See also [[Check_inventory_script]] for advice and a PHP script which can help fix some inventory type issues.</div>Aiaustinhttp://opensimulator.org/wiki/InventoryInventory2022-05-14T08:44:40Z<p>Aiaustin: Initial wiki page for inventory information and issues.</p>
<hr />
<div>Stub page for inventory issues.<br />
<br />
Prior to 2022 viewers accepted any form of inventory outside of completely malformed setups. Some viewers now have code designed to validate whether the inventory structure is properly setup. Earlier versions of OpenSim prior to 2015 have some inventory structure and folder types that can cause inventory to fail to load or can cause login to be blocked entirely.<br />
<br />
See [[https://jira.firestormviewer.org/browse/FIRE-31634|Firestorm JIRA FIRE-31634 Opensim - Inventory doesn't load on some grids]]<br />
<br />
See also [[Check_inventory_script]] for advice and a PHP script which can help fix some inventory type issues.</div>Aiaustinhttp://opensimulator.org/wiki/Next_GenNext Gen2022-02-17T09:06:54Z<p>Aiaustin: Rewording</p>
<hr />
<div>A wiki area to discuss possible features for a next generation OpenSimulator.<br />
<br />
There are many people and groups who have forks and OpenSim side projects. <br />
MisterBlue on 14-Feb-2022 suggested puling these side projects together and<br />
make a new and improved future OpenSimulator. <br />
<br />
MisterBlue is creating a new protocol and viewer that initially works with OpenSim. He feels that a new viewer is a first step toward breaking away from the chains of Second Life<br />
compatibility.<br />
<br />
A list of requested OpenSimulator features was collected from attendees of [http://conference.opensimulator.org/2021/ OpenSimulator Community Conference 2021 (OSCC21)]:<br />
<br />
* [[OSCC21_Suggestions|OSCC21 Audience Feature Suggestions]]<br />
<br />
==MisterBlue Notes==<br />
<br />
Misterblue 2022-02-15: This effort is to invigorate and advance OpenSimulator for the most recent rediscovery of metaverses.<br />
The goal is not to re-engineer everything to create the ultimate metaverse.<br />
The goal is to address large features that are limiting OpenSimulator's deployment and use.<br />
In the end, there will be a divergence from complete mimicking of SL.<br />
<br />
There will be incorporation of technologies like:<br />
<br />
* popular asset creation tools (mesh editors, UI designers, etc.),<br />
* standards (identity, avatar formats, asset formats, etc.),<br />
* modern infrastructures (containers, monitors, etc),<br />
* encryption (end-to-end encrypted chat, signed storage, etc), and<br />
* storage systems.<br />
<br />
That's all grand vision, but on more practical grounds, the OSCC21 feature suggestions break into:<br />
<br />
* Use of popular asset formats for world and avatars (FBX, GLTF, VRM, etc)<br />
* Use of popular external asset creation tools (Blender, etc)<br />
* Updated user documentation<br />
* Improved in-world asset representations (trees, PBM materials, etc)<br />
* Incorporation of hyped metaverse tech (VR, text-to-speech, etc)<br />
<br />
And add to these operational features:<br />
<br />
* Easy to install and operate (db management, etc)<br />
* Better access control (connection control, user management, etc)<br />
* Security (for inter-grid and inter-user communication)<br />
<br />
==Ai Austin Notes==<br />
<br />
AiAustin 2022-02-15<br />
* Next Generation Virtual Worlds – Social Web + Virtual Worlds + Content + People<br />
* To initially stay compatible with the current OpenSim platform, users and content would be helpful. Starting with an existing eco-system helps establish usage.<br />
* Suggest a strongly modular approach that allows for very different modules on different "regions" so that new forms of support can be provided on these, with fall backs when they are not available.<br />
* Low hanging fruit: Much better natural features like grass, plants, trees, rocks, water other than sea level, etc.. Possibly applied via over-layered textures over the terrain map, as in Unity3D.<br />
* Save and load "region" content (Next Gen OAR) direct conversion to and from modern content creation tools like Unity and Blender.<br />
* Establish a base for future metaverse interoperability by establishing and sharing initial experimentsl standards for avatar appearance and inventory content which can potentially be taken off the local platform via use of a '''Travel Outfit and Travel Pack''' (Next Gen Suitcase). See conceot of operations and ideas at [http://www.aiai.ed.ac.uk/~ai/resources/2022-01-13-OMI-Travel-Outfit-and-Pack/ OMI Open Metaverse Traversal Group – Austin Tate Presentation- 2022-01-13]<br />
<br />
== Standards and Metaverse Standard Groups That Might Be Interesting ==<br />
<br />
* [https://vrm-consortium.org VRM Avatars], [https://github.com/vrm-c Github]: humanoid avatar format popular with vtubers, VRChat, etc.<br />
* [https://en.m.wikipedia.org/wiki/GlTF Graphics Language Transmission Format (glTF)] : for avatars and 3D scenes.<br />
* [http://infinitemetaverse.com/ Infinite Metaverse Alliance]<br />
* [https://omigroup.org/home/ Open Metaverse Interoperability Group (OMI)]</div>Aiaustinhttp://opensimulator.org/wiki/Next_GenNext Gen2022-02-16T15:51:46Z<p>Aiaustin: /* Standards and Metaverse Standard Groups That Might Be Interesting */ Its Graphics plural in glTF</p>
<hr />
<div>A wiki area to discuss a possible next generation OpenSimulator.<br />
<br />
There are many people and groups who have forks and OpenSim side projects. <br />
MisterBlue on 14-Feb-2022 suggested puling these side projects together and<br />
make a new and improved future OpenSimulator. <br />
<br />
MisterBlue is creating a new protocol and viewer that initially works with OpenSim.<br />
A new viewer is a first step toward breaking away from the chains of Second Life<br />
compatibility.<br />
<br />
The list of requested features from attendees of [http://conference.opensimulator.org/2021/ OpenSimulator Community Conference 2021 (OSCC21)] is a good starting point.<br />
<br />
* [[OSCC21_Suggestions|OSCC21 Audience Feature Suggestions]]<br />
<br />
==MisterBlue Notes==<br />
<br />
Misterblue 2022-02-15: This effort is to invigorate and advance OpenSimulator for the most recent rediscovery of metaverses.<br />
The goal is not to re-engineer everything to create the ultimate metaverse.<br />
The goal is to address large features that are limiting OpenSimulator's deployment and use.<br />
In the end, there will be a divergence from complete mimicking of SL.<br />
<br />
There will be incorporation of technologies like:<br />
<br />
* popular asset creation tools (mesh editors, UI designers, etc.),<br />
* standards (identity, avatar formats, asset formats, etc.),<br />
* modern infrastructures (containers, monitors, etc),<br />
* encryption (end-to-end encrypted chat, signed storage, etc), and<br />
* storage systems.<br />
<br />
That's all grand vision, but on more practical grounds, the OSCC21 feature suggestions break into:<br />
<br />
* Use of popular asset formats for world and avatars (FBX, GLTF, VRM, etc)<br />
* Use of popular external asset creation tools (Blender, etc)<br />
* Updated user documentation<br />
* Improved in-world asset representations (trees, PBM materials, etc)<br />
* Incorporation of hyped metaverse tech (VR, text-to-speech, etc)<br />
<br />
And add to these operational features:<br />
<br />
* Easy to install and operate (db management, etc)<br />
* Better access control (connection control, user management, etc)<br />
* Security (for inter-grid and inter-user communication)<br />
<br />
==Ai Austin Notes==<br />
<br />
AiAustin 2022-02-15<br />
* Next Generation Virtual Worlds – Social Web + Virtual Worlds + Content + People<br />
* To initially stay compatible with the current OpenSim platform, users and content would be helpful. Starting with an existing eco-system helps establish usage.<br />
* Suggest a strongly modular approach that allows for very different modules on different "regions" so that new forms of support can be provided on these, with fall backs when they are not available.<br />
* Low hanging fruit: Much better natural features like grass, plants, trees, rocks, water other than sea level, etc.. Possibly applied via over-layered textures over the terrain map, as in Unity3D.<br />
* Save and load "region" content (Next Gen OAR) direct conversion to and from modern content creation tools like Unity and Blender.<br />
* Establish a base for future metaverse interoperability by establishing and sharing initial experimentsl standards for avatar appearance and inventory content which can potentially be taken off the local platform via use of a '''Travel Outfit and Travel Pack''' (Next Gen Suitcase). See conceot of operations and ideas at [http://www.aiai.ed.ac.uk/~ai/resources/2022-01-13-OMI-Travel-Outfit-and-Pack/ OMI Open Metaverse Traversal Group – Austin Tate Presentation- 2022-01-13]<br />
<br />
== Standards and Metaverse Standard Groups That Might Be Interesting ==<br />
<br />
* [https://vrm-consortium.org VRM Avatars], [https://github.com/vrm-c Github]: humanoid avatar format popular with vtubers, VRChat, etc.<br />
* [https://en.m.wikipedia.org/wiki/GlTF Graphics Language Transmission Format (glTF)] : for avatars and 3D scenes.<br />
* [http://infinitemetaverse.com/ Infinite Metaverse Alliance]<br />
* [https://omigroup.org/home/ Open Metaverse Interoperability Group (OMI)]</div>Aiaustinhttp://opensimulator.org/wiki/OSCC21_SuggestionsOSCC21 Suggestions2022-02-15T22:17:19Z<p>Aiaustin: Expand what Meta line means</p>
<hr />
<div>From Joreon Frans (Frans Charming in OpenSim) who helped organize [[http://conference.opensimulator.org/2021 OSCC21]]. The audience was asked what features they would like to see in OpenSimulator. Keep in mind these were informally asked from the audience which could be either viewer or server features.<br />
<br />
'''What Feature would you like to see in OpenSimulator'''<br />
<br />
#PBR Textures are only partially supported. It would be nice to see more of a full implementation to help bring the virtual world up to the standards of the gaming industry that people see when they play games.<br />
#It would be great if Open Simulator could import other formats like FBX and/or glTF with mesh, textures, and animations able to be uploaded. That would make content a lot easier to bring in than the archaic BVH and DAE formats. Most modern virtual worlds work with these formats and some even have ability to pull from places like 3dwarehouse and import avatars from http://readyplayer.me.<br />
#The OpenSimulator-Wiki needs a massive update. Most pages are either confusing, outdated or incomplete. E.g. the page 'Upgrading': It says "We don't recommend that you simply copy over your existing config files". Some lines below it says "Copy... The opensim.ini file". And it says "SEE Discussion at Talk:Upgrading". When you take a look at this, it says "last modified on 24 July 2009". And why are instructions at all in a discussion page? The Wiki software itself is outdated, it is from 2013. The privacy policy page is empty. It requires also RL name and address of at least one person who is responsible for the website and its content. Without that it is neither legal nor trustworthy. The same applies to the Mantis. Ever tried to delete your account or the contact an admin via email? Not working. Do you think, anyone will ever take OpenSimulator seriously?<br />
#On performing a HG teleport it should be possible to access the welcome page of the target grid.<br />
#One button publishing of Blender scenes to opensim. There was once a project to allow people to build a scene in Blender and publish it to opensim / realextend. There are now many many more Blender users and there is now a huge interest in the metaverse. This is an opensource project. It could be resurrected and bring a LOT of Blender builders into opensim. Here's the code: https://github.com/b2rex/b2rex. Here's a youtube description: https://www.youtube.com/watch?v=8biEltPsWT8<br />
#A massively improved system avatar, which makes mesh avatars obsolete ? (at least partially). Those system avatars are easier to customize, so new/older/handicapped users have it easier.<br />
#Copy Complete Prim and Mirror Complete prim buttons or options for symmetrical builds<br />
#Having the system trees and plants (those in Build window) updated to properly made mesh objects, but also in the ways they automatically snap to ground level. And when it is grass, it still has to be placed as mats keeping the terrain topology. So we have a bunch of standard plants in our libraries. At least you should publish information on how we can change the library content by yourself.<br />
#Chat decoupled from core<br />
#Secure HG protocol decoupled from core<br />
#Code modernization/modularization<br />
#Community support<br />
#Interoperability with other platforms<br />
#TTS and STT<br />
#Instead of new features I would like to see getting the many bugs fixed and the installation simplified, because so many people just want a local grid for content creation or tinkering. if we could make it easier for them, they would also become members of the HG community one by one.<br />
#The platform is an amazing and suitable tool for learning and social networking by design. The viewer is extremely powerful and has very advanced features, and as such, can generally be considered rather insanely difficult to get into for a new user. Viewer devs and grid runners could form teams, or otherwise work together, to ensure new users have access to that information globally, from the beginning. Things like object creation, inventory usage and avatar customization should be more accessible to all users.<br />
#Easy access viewer for new users<br />
#New renderer that can support VR - and then a Steam VR, Quest 2 or VRML client.<br />
#A kind of .htaccess, so I can route teleport attempts to deleted or renamed regions to another region. It should in addition send a message to the avatar, and tell him to create a new landmark.<br />
#A way for scripters to know under what opensim environment / scripting engine the region is running under . There is a OSL function to have that reported but its rights of use is too restrictive . It should be allowed to know that information from any scripts since X and Y engines are not fully compatible .<br />
#Programmable TEXT-TO-SPEECH in the Viewer with associated VISEMES for AV mouth , face and eyebrows...<br />
#Meta (impact of Facebook work on "Metaverse")<br />
#Since most grids are just small standalone ones with hypergrid enabled, there is no need for huge MySQL databases. Running, setting up and maintaining a MySQL instance is far over the top for such tiny grids. SQLite is sufficient. The only problem which remains, is that the asset database blows up quickly, because content deleted from all regions and inventories remains forever in the database. If you are into content creation and upload mesh and textures very often, the database blows up to several GB, where maybe less than one is required. Therefor a kind of garbage collector would be fine. (one can export and reimport regions and profiles into empty databases, but that breaks up too much and is too complex for most grid owners). So I want to see - a garbage collector for the asset database - SQLite support for groups<br />
#Support for Shared Environments that Firestorm has build in.</div>Aiaustinhttp://opensimulator.org/wiki/Next_GenNext Gen2022-02-15T22:13:12Z<p>Aiaustin: /* Standards and Metaverse Standard Groups That Might Be Interesting */</p>
<hr />
<div>A wiki area to discuss a possible next generation OpenSimulator.<br />
<br />
There are many people and groups who have forks and OpenSim side projects. <br />
MisterBlue on 14-Feb-2022 suggested puling these side projects together and<br />
make a new and improved future OpenSimulator. <br />
<br />
MisterBlue is creating a new protocol and viewer that initially works with OpenSim.<br />
A new viewer is a first step toward breaking away from the chains of Second Life<br />
compatibility.<br />
<br />
The list of requested features from attendees of [http://conference.opensimulator.org/2021/ OpenSimulator Community Conference 2021 (OSCC21)] is a good starting point.<br />
<br />
* [[OSCC21_Suggestions|OSCC21 Audience Feature Suggestions]]<br />
<br />
==MisterBlue Notes==<br />
<br />
Misterblue 2022-02-15: This effort is to invigorate and advance OpenSimulator for the most recent rediscovery of metaverses.<br />
The goal is not to re-engineer everything to create the ultimate metaverse.<br />
The goal is to address large features that are limiting OpenSimulator's deployment and use.<br />
In the end, there will be a divergence from complete mimicking of SL.<br />
<br />
There will be incorporation of technologies like:<br />
<br />
* popular asset creation tools (mesh editors, UI designers, etc.),<br />
* standards (identity, avatar formats, asset formats, etc.),<br />
* modern infrastructures (containers, monitors, etc),<br />
* encryption (end-to-end encrypted chat, signed storage, etc), and<br />
* storage systems.<br />
<br />
That's all grand vision, but on more practical grounds, the OSCC21 feature suggestions break into:<br />
<br />
* Use of popular asset formats for world and avatars (FBX, GLTF, VRM, etc)<br />
* Use of popular external asset creation tools (Blender, etc)<br />
* Updated user documentation<br />
* Improved in-world asset representations (trees, PBM materials, etc)<br />
* Incorporation of hyped metaverse tech (VR, text-to-speech, etc)<br />
<br />
And add to these operational features:<br />
<br />
* Easy to install and operate (db management, etc)<br />
* Better access control (connection control, user management, etc)<br />
* Security (for inter-grid and inter-user communication)<br />
<br />
==Ai Austin Notes==<br />
<br />
AiAustin 2022-02-15<br />
* Next Generation Virtual Worlds – Social Web + Virtual Worlds + Content + People<br />
* To initially stay compatible with the current OpenSim platform, users and content would be helpful. Starting with an existing eco-system helps establish usage.<br />
* Suggest a strongly modular approach that allows for very different modules on different "regions" so that new forms of support can be provided on these, with fall backs when they are not available.<br />
* Low hanging fruit: Much better natural features like grass, plants, trees, rocks, water other than sea level, etc.. Possibly applied via over-layered textures over the terrain map, as in Unity3D.<br />
* Save and load "region" content (Next Gen OAR) direct conversion to and from modern content creation tools like Unity and Blender.<br />
* Establish a base for future metaverse interoperability by establishing and sharing initial experimentsl standards for avatar appearance and inventory content which can potentially be taken off the local platform via use of a '''Travel Outfit and Travel Pack''' (Next Gen Suitcase). See conceot of operations and ideas at [http://www.aiai.ed.ac.uk/~ai/resources/2022-01-13-OMI-Travel-Outfit-and-Pack/ OMI Open Metaverse Traversal Group – Austin Tate Presentation- 2022-01-13]<br />
<br />
== Standards and Metaverse Standard Groups That Might Be Interesting ==<br />
<br />
* [https://vrm-consortium.org VRM Avatars], [https://github.com/vrm-c Github]: humanoid avatar format popular with vtubers, VRChat, etc.<br />
* [https://en.m.wikipedia.org/wiki/GlTF Graphic Language Transmission Format (glTF)] : for avatars and 3D scenes.<br />
* [http://infinitemetaverse.com/ Infinite Metaverse Alliance]<br />
* [https://omigroup.org/home/ Open Metaverse Interoperability Group (OMI)]</div>Aiaustinhttp://opensimulator.org/wiki/Next_GenNext Gen2022-02-15T22:12:10Z<p>Aiaustin: added glTF/* Standards and Metaverse Standard Groups That Might Be Interesting */</p>
<hr />
<div>A wiki area to discuss a possible next generation OpenSimulator.<br />
<br />
There are many people and groups who have forks and OpenSim side projects. <br />
MisterBlue on 14-Feb-2022 suggested puling these side projects together and<br />
make a new and improved future OpenSimulator. <br />
<br />
MisterBlue is creating a new protocol and viewer that initially works with OpenSim.<br />
A new viewer is a first step toward breaking away from the chains of Second Life<br />
compatibility.<br />
<br />
The list of requested features from attendees of [http://conference.opensimulator.org/2021/ OpenSimulator Community Conference 2021 (OSCC21)] is a good starting point.<br />
<br />
* [[OSCC21_Suggestions|OSCC21 Audience Feature Suggestions]]<br />
<br />
==MisterBlue Notes==<br />
<br />
Misterblue 2022-02-15: This effort is to invigorate and advance OpenSimulator for the most recent rediscovery of metaverses.<br />
The goal is not to re-engineer everything to create the ultimate metaverse.<br />
The goal is to address large features that are limiting OpenSimulator's deployment and use.<br />
In the end, there will be a divergence from complete mimicking of SL.<br />
<br />
There will be incorporation of technologies like:<br />
<br />
* popular asset creation tools (mesh editors, UI designers, etc.),<br />
* standards (identity, avatar formats, asset formats, etc.),<br />
* modern infrastructures (containers, monitors, etc),<br />
* encryption (end-to-end encrypted chat, signed storage, etc), and<br />
* storage systems.<br />
<br />
That's all grand vision, but on more practical grounds, the OSCC21 feature suggestions break into:<br />
<br />
* Use of popular asset formats for world and avatars (FBX, GLTF, VRM, etc)<br />
* Use of popular external asset creation tools (Blender, etc)<br />
* Updated user documentation<br />
* Improved in-world asset representations (trees, PBM materials, etc)<br />
* Incorporation of hyped metaverse tech (VR, text-to-speech, etc)<br />
<br />
And add to these operational features:<br />
<br />
* Easy to install and operate (db management, etc)<br />
* Better access control (connection control, user management, etc)<br />
* Security (for inter-grid and inter-user communication)<br />
<br />
==Ai Austin Notes==<br />
<br />
AiAustin 2022-02-15<br />
* Next Generation Virtual Worlds – Social Web + Virtual Worlds + Content + People<br />
* To initially stay compatible with the current OpenSim platform, users and content would be helpful. Starting with an existing eco-system helps establish usage.<br />
* Suggest a strongly modular approach that allows for very different modules on different "regions" so that new forms of support can be provided on these, with fall backs when they are not available.<br />
* Low hanging fruit: Much better natural features like grass, plants, trees, rocks, water other than sea level, etc.. Possibly applied via over-layered textures over the terrain map, as in Unity3D.<br />
* Save and load "region" content (Next Gen OAR) direct conversion to and from modern content creation tools like Unity and Blender.<br />
* Establish a base for future metaverse interoperability by establishing and sharing initial experimentsl standards for avatar appearance and inventory content which can potentially be taken off the local platform via use of a '''Travel Outfit and Travel Pack''' (Next Gen Suitcase). See conceot of operations and ideas at [http://www.aiai.ed.ac.uk/~ai/resources/2022-01-13-OMI-Travel-Outfit-and-Pack/ OMI Open Metaverse Traversal Group – Austin Tate Presentation- 2022-01-13]<br />
<br />
== Standards and Metaverse Standard Groups That Might Be Interesting ==<br />
<br />
* [https://vrm-consortium.org VRM Avatars], [https://github.com/vrm-c Github]: humanoid avatar format popular with vtubers, VRChat, etc.<br />
* [https://en.m.wikipedia.org/wiki/GlTF Graphic Language Transmission Format (glTF)] : transmissiin firmat fir avatars and 3D scenes.<br />
* [http://infinitemetaverse.com/ Infinite Metaverse Alliance]<br />
* [https://omigroup.org/home/ Open Metaverse Interoperability Group (OMI)]</div>Aiaustinhttp://opensimulator.org/wiki/Next_GenNext Gen2022-02-15T22:07:35Z<p>Aiaustin: /* Standards and Metaverse Standard Groups That Might Be Interesting */</p>
<hr />
<div>A wiki area to discuss a possible next generation OpenSimulator.<br />
<br />
There are many people and groups who have forks and OpenSim side projects. <br />
MisterBlue on 14-Feb-2022 suggested puling these side projects together and<br />
make a new and improved future OpenSimulator. <br />
<br />
MisterBlue is creating a new protocol and viewer that initially works with OpenSim.<br />
A new viewer is a first step toward breaking away from the chains of Second Life<br />
compatibility.<br />
<br />
The list of requested features from attendees of [http://conference.opensimulator.org/2021/ OpenSimulator Community Conference 2021 (OSCC21)] is a good starting point.<br />
<br />
* [[OSCC21_Suggestions|OSCC21 Audience Feature Suggestions]]<br />
<br />
==MisterBlue Notes==<br />
<br />
Misterblue 2022-02-15: This effort is to invigorate and advance OpenSimulator for the most recent rediscovery of metaverses.<br />
The goal is not to re-engineer everything to create the ultimate metaverse.<br />
The goal is to address large features that are limiting OpenSimulator's deployment and use.<br />
In the end, there will be a divergence from complete mimicking of SL.<br />
<br />
There will be incorporation of technologies like:<br />
<br />
* popular asset creation tools (mesh editors, UI designers, etc.),<br />
* standards (identity, avatar formats, asset formats, etc.),<br />
* modern infrastructures (containers, monitors, etc),<br />
* encryption (end-to-end encrypted chat, signed storage, etc), and<br />
* storage systems.<br />
<br />
That's all grand vision, but on more practical grounds, the OSCC21 feature suggestions break into:<br />
<br />
* Use of popular asset formats for world and avatars (FBX, GLTF, VRM, etc)<br />
* Use of popular external asset creation tools (Blender, etc)<br />
* Updated user documentation<br />
* Improved in-world asset representations (trees, PBM materials, etc)<br />
* Incorporation of hyped metaverse tech (VR, text-to-speech, etc)<br />
<br />
And add to these operational features:<br />
<br />
* Easy to install and operate (db management, etc)<br />
* Better access control (connection control, user management, etc)<br />
* Security (for inter-grid and inter-user communication)<br />
<br />
==Ai Austin Notes==<br />
<br />
AiAustin 2022-02-15<br />
* Next Generation Virtual Worlds – Social Web + Virtual Worlds + Content + People<br />
* To initially stay compatible with the current OpenSim platform, users and content would be helpful. Starting with an existing eco-system helps establish usage.<br />
* Suggest a strongly modular approach that allows for very different modules on different "regions" so that new forms of support can be provided on these, with fall backs when they are not available.<br />
* Low hanging fruit: Much better natural features like grass, plants, trees, rocks, water other than sea level, etc.. Possibly applied via over-layered textures over the terrain map, as in Unity3D.<br />
* Save and load "region" content (Next Gen OAR) direct conversion to and from modern content creation tools like Unity and Blender.<br />
* Establish a base for future metaverse interoperability by establishing and sharing initial experimentsl standards for avatar appearance and inventory content which can potentially be taken off the local platform via use of a '''Travel Outfit and Travel Pack''' (Next Gen Suitcase). See conceot of operations and ideas at [http://www.aiai.ed.ac.uk/~ai/resources/2022-01-13-OMI-Travel-Outfit-and-Pack/ OMI Open Metaverse Traversal Group – Austin Tate Presentation- 2022-01-13]<br />
<br />
== Standards and Metaverse Standard Groups That Might Be Interesting ==<br />
<br />
* [https://vrm-consortium.org VRM Avatars], [https://github.com/vrm-c Github]: humanoid avatar format popular with vtubers, VRChat, etc<br />
* [http://infinitemetaverse.com/ Infinite Metaverse Alliance]<br />
* [https://omigroup.org/home/ Open Metaverse Interoperability Group (OMI)]</div>Aiaustinhttp://opensimulator.org/wiki/Next_GenNext Gen2022-02-15T22:05:39Z<p>Aiaustin: /* Ai Austin Notes */</p>
<hr />
<div>A wiki area to discuss a possible next generation OpenSimulator.<br />
<br />
There are many people and groups who have forks and OpenSim side projects. <br />
MisterBlue on 14-Feb-2022 suggested puling these side projects together and<br />
make a new and improved future OpenSimulator. <br />
<br />
MisterBlue is creating a new protocol and viewer that initially works with OpenSim.<br />
A new viewer is a first step toward breaking away from the chains of Second Life<br />
compatibility.<br />
<br />
The list of requested features from attendees of [http://conference.opensimulator.org/2021/ OpenSimulator Community Conference 2021 (OSCC21)] is a good starting point.<br />
<br />
* [[OSCC21_Suggestions|OSCC21 Audience Feature Suggestions]]<br />
<br />
==MisterBlue Notes==<br />
<br />
Misterblue 2022-02-15: This effort is to invigorate and advance OpenSimulator for the most recent rediscovery of metaverses.<br />
The goal is not to re-engineer everything to create the ultimate metaverse.<br />
The goal is to address large features that are limiting OpenSimulator's deployment and use.<br />
In the end, there will be a divergence from complete mimicking of SL.<br />
<br />
There will be incorporation of technologies like:<br />
<br />
* popular asset creation tools (mesh editors, UI designers, etc.),<br />
* standards (identity, avatar formats, asset formats, etc.),<br />
* modern infrastructures (containers, monitors, etc),<br />
* encryption (end-to-end encrypted chat, signed storage, etc), and<br />
* storage systems.<br />
<br />
That's all grand vision, but on more practical grounds, the OSCC21 feature suggestions break into:<br />
<br />
* Use of popular asset formats for world and avatars (FBX, GLTF, VRM, etc)<br />
* Use of popular external asset creation tools (Blender, etc)<br />
* Updated user documentation<br />
* Improved in-world asset representations (trees, PBM materials, etc)<br />
* Incorporation of hyped metaverse tech (VR, text-to-speech, etc)<br />
<br />
And add to these operational features:<br />
<br />
* Easy to install and operate (db management, etc)<br />
* Better access control (connection control, user management, etc)<br />
* Security (for inter-grid and inter-user communication)<br />
<br />
==Ai Austin Notes==<br />
<br />
AiAustin 2022-02-15<br />
* Next Generation Virtual Worlds – Social Web + Virtual Worlds + Content + People<br />
* To initially stay compatible with the current OpenSim platform, users and content would be helpful. Starting with an existing eco-system helps establish usage.<br />
* Suggest a strongly modular approach that allows for very different modules on different "regions" so that new forms of support can be provided on these, with fall backs when they are not available.<br />
* Low hanging fruit: Much better natural features like grass, plants, trees, rocks, water other than sea level, etc.. Possibly applied via over-layered textures over the terrain map, as in Unity3D.<br />
* Save and load "region" content (Next Gen OAR) direct conversion to and from modern content creation tools like Unity and Blender.<br />
* Establish a base for future metaverse interoperability by establishing and sharing initial experimentsl standards for avatar appearance and inventory content which can potentially be taken off the local platform via use of a '''Travel Outfit and Travel Pack''' (Next Gen Suitcase). See conceot of operations and ideas at [http://www.aiai.ed.ac.uk/~ai/resources/2022-01-13-OMI-Travel-Outfit-and-Pack/ OMI Open Metaverse Traversal Group – Austin Tate Presentation- 2022-01-13]<br />
<br />
== Standards and Metaverse Standard Groups That Might Be Interesting ==<br />
<br />
* [https://vrm-consortium.org VRM Avatars], [https://github.com/vrm-c Github]: humanoid avatar format popular with vtubers, VRChat, etc<br />
* [http://infinitemetaverse.com/ Infinite Metaverse Alliance]<br />
* [https://omigroup.org/home/ Open Metaverse Interoperability Group]</div>Aiaustinhttp://opensimulator.org/wiki/Next_GenNext Gen2022-02-15T22:04:48Z<p>Aiaustin: /* Ai Austin Notes */ fiormat</p>
<hr />
<div>A wiki area to discuss a possible next generation OpenSimulator.<br />
<br />
There are many people and groups who have forks and OpenSim side projects. <br />
MisterBlue on 14-Feb-2022 suggested puling these side projects together and<br />
make a new and improved future OpenSimulator. <br />
<br />
MisterBlue is creating a new protocol and viewer that initially works with OpenSim.<br />
A new viewer is a first step toward breaking away from the chains of Second Life<br />
compatibility.<br />
<br />
The list of requested features from attendees of [http://conference.opensimulator.org/2021/ OpenSimulator Community Conference 2021 (OSCC21)] is a good starting point.<br />
<br />
* [[OSCC21_Suggestions|OSCC21 Audience Feature Suggestions]]<br />
<br />
==MisterBlue Notes==<br />
<br />
Misterblue 2022-02-15: This effort is to invigorate and advance OpenSimulator for the most recent rediscovery of metaverses.<br />
The goal is not to re-engineer everything to create the ultimate metaverse.<br />
The goal is to address large features that are limiting OpenSimulator's deployment and use.<br />
In the end, there will be a divergence from complete mimicking of SL.<br />
<br />
There will be incorporation of technologies like:<br />
<br />
* popular asset creation tools (mesh editors, UI designers, etc.),<br />
* standards (identity, avatar formats, asset formats, etc.),<br />
* modern infrastructures (containers, monitors, etc),<br />
* encryption (end-to-end encrypted chat, signed storage, etc), and<br />
* storage systems.<br />
<br />
That's all grand vision, but on more practical grounds, the OSCC21 feature suggestions break into:<br />
<br />
* Use of popular asset formats for world and avatars (FBX, GLTF, VRM, etc)<br />
* Use of popular external asset creation tools (Blender, etc)<br />
* Updated user documentation<br />
* Improved in-world asset representations (trees, PBM materials, etc)<br />
* Incorporation of hyped metaverse tech (VR, text-to-speech, etc)<br />
<br />
And add to these operational features:<br />
<br />
* Easy to install and operate (db management, etc)<br />
* Better access control (connection control, user management, etc)<br />
* Security (for inter-grid and inter-user communication)<br />
<br />
==Ai Austin Notes==<br />
<br />
AiAustin 2022-02-15<br />
* Next Generation Virtual Worlds – Social Web + Virtual Worlds + Content + People<br />
* To initially stay compatible with the current OpenSim platform, users and content would be helpful. Starting with an existing eco-system helps establish usage.<br />
* Suggest a strongly modular approach that allows for very different modules on different "regions" so that new forms of support can be provided on these, with fall backs when they are not available.<br />
* Low hanging fruit: Much better natural features like grass, plants, trees, rocks, water other than sea level, etc.. Possibly applied via over-layered textures over the terrain map, as in Unity3D.<br />
* Save and load "region" content (Next Gen OAR) direct conversion to and from modern content creation tools like Unity and Blender.<br />
* Establish a base for future metaverse interoperability by establishing and sharing initial experimentsl standards for avatar appearance and inventory content which can potentially be taken off the local platform via use of a '''Travel Outfit and Travel Pack''' (Next Gen Suitcase)[http://www.aiai.ed.ac.uk/~ai/resources/2022-01-13-OMI-Travel-Outfit-and-Pack/ OMI Open Metaverse Traversal Group – Austin Tate Presentation- 2022-01-13]<br />
<br />
== Standards and Metaverse Standard Groups That Might Be Interesting ==<br />
<br />
* [https://vrm-consortium.org VRM Avatars], [https://github.com/vrm-c Github]: humanoid avatar format popular with vtubers, VRChat, etc<br />
* [http://infinitemetaverse.com/ Infinite Metaverse Alliance]<br />
* [https://omigroup.org/home/ Open Metaverse Interoperability Group]</div>Aiaustinhttp://opensimulator.org/wiki/Next_GenNext Gen2022-02-15T22:02:23Z<p>Aiaustin: /* Ai Austin Notes */ added travel outfit and travel pack line and link.</p>
<hr />
<div>A wiki area to discuss a possible next generation OpenSimulator.<br />
<br />
There are many people and groups who have forks and OpenSim side projects. <br />
MisterBlue on 14-Feb-2022 suggested puling these side projects together and<br />
make a new and improved future OpenSimulator. <br />
<br />
MisterBlue is creating a new protocol and viewer that initially works with OpenSim.<br />
A new viewer is a first step toward breaking away from the chains of Second Life<br />
compatibility.<br />
<br />
The list of requested features from attendees of [http://conference.opensimulator.org/2021/ OpenSimulator Community Conference 2021 (OSCC21)] is a good starting point.<br />
<br />
* [[OSCC21_Suggestions|OSCC21 Audience Feature Suggestions]]<br />
<br />
==MisterBlue Notes==<br />
<br />
Misterblue 2022-02-15: This effort is to invigorate and advance OpenSimulator for the most recent rediscovery of metaverses.<br />
The goal is not to re-engineer everything to create the ultimate metaverse.<br />
The goal is to address large features that are limiting OpenSimulator's deployment and use.<br />
In the end, there will be a divergence from complete mimicking of SL.<br />
<br />
There will be incorporation of technologies like:<br />
<br />
* popular asset creation tools (mesh editors, UI designers, etc.),<br />
* standards (identity, avatar formats, asset formats, etc.),<br />
* modern infrastructures (containers, monitors, etc),<br />
* encryption (end-to-end encrypted chat, signed storage, etc), and<br />
* storage systems.<br />
<br />
That's all grand vision, but on more practical grounds, the OSCC21 feature suggestions break into:<br />
<br />
* Use of popular asset formats for world and avatars (FBX, GLTF, VRM, etc)<br />
* Use of popular external asset creation tools (Blender, etc)<br />
* Updated user documentation<br />
* Improved in-world asset representations (trees, PBM materials, etc)<br />
* Incorporation of hyped metaverse tech (VR, text-to-speech, etc)<br />
<br />
And add to these operational features:<br />
<br />
* Easy to install and operate (db management, etc)<br />
* Better access control (connection control, user management, etc)<br />
* Security (for inter-grid and inter-user communication)<br />
<br />
==Ai Austin Notes==<br />
<br />
AiAustin 2022-02-15<br />
* Next Generation Virtual Worlds – Social Web + Virtual Worlds + Content + People<br />
* To initially stay compatible with the current OpenSim platform, users and content would be helpful. Starting with an existing eco-system helps establish usage.<br />
* Suggest a strongly modular approach that allows for very different modules on different "regions" so that new forms of support can be provided on these, with fall backs when they are not available.<br />
* Low hanging fruit: Much better natural features like grass, plants, trees, rocks, water other than sea level, etc.. Possibly applied via over-layered textures over the terrain map, as in Unity3D.<br />
* Save and load "region" content (Next Gen OAR) direct conversion to and from modern content creation tools like Unity and Blender.<br />
* Establish a base for future metaverse interoperability by setting a baais for avatar appearance and inventory content which potentially be taken off the local platform via (Next Gen Suitcase). Use of a '''Travel Outfit and Travel Pack'''. [http://www.aiai.ed.ac.uk/~ai/resources/2022-01-13-OMI-Travel-Outfit-and-Pack/ OMI Open Metaverse Traversal Group – Austin Tate Presentation- 2022-01-13]<br />
<br />
== Standards and Metaverse Standard Groups That Might Be Interesting ==<br />
<br />
* [https://vrm-consortium.org VRM Avatars], [https://github.com/vrm-c Github]: humanoid avatar format popular with vtubers, VRChat, etc<br />
* [http://infinitemetaverse.com/ Infinite Metaverse Alliance]<br />
* [https://omigroup.org/home/ Open Metaverse Interoperability Group]</div>Aiaustinhttp://opensimulator.org/wiki/Next_GenNext Gen2022-02-15T21:52:35Z<p>Aiaustin: /* Ai Austin Notes */ Correct date</p>
<hr />
<div>A wiki area to discuss a possible next generation OpenSimulator.<br />
<br />
There are many people and groups who have forks and OpenSim side projects. <br />
MisterBlue on 14-Feb-2022 suggested puling these side projects together and<br />
make a new and improved future OpenSimulator. <br />
<br />
MisterBlue is creating a new protocol and viewer that initially works with OpenSim.<br />
A new viewer is a first step toward breaking away from the chains of Second Life<br />
compatibility.<br />
<br />
The list of requested features from attendees of [http://conference.opensimulator.org/2021/ OpenSimulator Community Conference 2021 (OSCC21)] is a good starting point.<br />
<br />
* [[OSCC21_Suggestions|OSCC21 Audience Feature Suggestions]]<br />
<br />
==MisterBlue Notes==<br />
<br />
Misterblue 2022-02-15: This effort is to invigorate and advance OpenSimulator for the most recent rediscovery of metaverses.<br />
The goal is not to re-engineer everything to create the ultimate metaverse.<br />
The goal is to address large features that are limiting OpenSimulator's deployment and use.<br />
In the end, there will be a divergence from complete mimicking of SL.<br />
<br />
There will be incorporation of technologies like:<br />
<br />
* popular asset creation tools (mesh editors, UI designers, etc.),<br />
* standards (identity, avatar formats, asset formats, etc.),<br />
* modern infrastructures (containers, monitors, etc),<br />
* encryption (end-to-end encrypted chat, signed storage, etc), and<br />
* storage systems.<br />
<br />
That's all grand vision, but on more practical grounds, the OSCC21 feature suggestions break into:<br />
<br />
* Use of popular asset formats for world and avatars (FBX, GLTF, VRM, etc)<br />
* Use of popular external asset creation tools (Blender, etc)<br />
* Updated user documentation<br />
* Improved in-world asset representations (trees, PBM materials, etc)<br />
* Incorporation of hyped metaverse tech (VR, text-to-speech, etc)<br />
<br />
And add to these operational features:<br />
<br />
* Easy to install and operate (db management, etc)<br />
* Better access control (connection control, user management, etc)<br />
* Security (for inter-grid and inter-user communication)<br />
<br />
==Ai Austin Notes==<br />
<br />
AiAustin 2022-02-15<br />
* To initially stay compatible with the current OpenSim platform, users and content would be helpful. Starting with an existing eco-system helps establish usage.<br />
* Suggest a strongly modular approach that allows for very different modules on different "regions" so that new forms of support can be provided on these, with fall backs when they are not available.<br />
* Low hanging fruit: Much better natural features like grass, plants, trees, rocks, water other than sea level, etc.. Possibly applied via over-layered textures over the terrain map, as in Unity3D.<br />
* Save and load "region" content (Next Gen OAR) direct conversion to and from modern content creation tools like Unity and Blender.<br />
<br />
== Standards and Metaverse Standard Groups That Might Be Interesting ==<br />
<br />
* [https://vrm-consortium.org VRM Avatars], [https://github.com/vrm-c Github]: humanoid avatar format popular with vtubers, VRChat, etc<br />
* [http://infinitemetaverse.com/ Infinite Metaverse Alliance]<br />
* [https://omigroup.org/home/ Open Metaverse Interoperability Group]</div>Aiaustinhttp://opensimulator.org/wiki/Next_GenNext Gen2022-02-15T21:51:12Z<p>Aiaustin: /* Ai Austin Notes */</p>
<hr />
<div>A wiki area to discuss a possible next generation OpenSimulator.<br />
<br />
There are many people and groups who have forks and OpenSim side projects. <br />
MisterBlue on 14-Feb-2022 suggested puling these side projects together and<br />
make a new and improved future OpenSimulator. <br />
<br />
MisterBlue is creating a new protocol and viewer that initially works with OpenSim.<br />
A new viewer is a first step toward breaking away from the chains of Second Life<br />
compatibility.<br />
<br />
The list of requested features from attendees of [http://conference.opensimulator.org/2021/ OpenSimulator Community Conference 2021 (OSCC21)] is a good starting point.<br />
<br />
* [[OSCC21_Suggestions|OSCC21 Audience Feature Suggestions]]<br />
<br />
==MisterBlue Notes==<br />
<br />
Misterblue 2022-02-15: This effort is to invigorate and advance OpenSimulator for the most recent rediscovery of metaverses.<br />
The goal is not to re-engineer everything to create the ultimate metaverse.<br />
The goal is to address large features that are limiting OpenSimulator's deployment and use.<br />
In the end, there will be a divergence from complete mimicking of SL.<br />
<br />
There will be incorporation of technologies like:<br />
<br />
* popular asset creation tools (mesh editors, UI designers, etc.),<br />
* standards (identity, avatar formats, asset formats, etc.),<br />
* modern infrastructures (containers, monitors, etc),<br />
* encryption (end-to-end encrypted chat, signed storage, etc), and<br />
* storage systems.<br />
<br />
That's all grand vision, but on more practical grounds, the OSCC21 feature suggestions break into:<br />
<br />
* Use of popular asset formats for world and avatars (FBX, GLTF, VRM, etc)<br />
* Use of popular external asset creation tools (Blender, etc)<br />
* Updated user documentation<br />
* Improved in-world asset representations (trees, PBM materials, etc)<br />
* Incorporation of hyped metaverse tech (VR, text-to-speech, etc)<br />
<br />
And add to these operational features:<br />
<br />
* Easy to install and operate (db management, etc)<br />
* Better access control (connection control, user management, etc)<br />
* Security (for inter-grid and inter-user communication)<br />
<br />
==Ai Austin Notes==<br />
<br />
AiAustin 2922-02-15<br />
* To initially stay compatible with the current OpenSim platform, users and content would be helpful. Starting with an existing eco-system helps establish usage.<br />
* Suggest a strongly modular approach that allows for very different modules on different "regions" so that new forms of support can be provided on these, with fall backs when they are not available.<br />
* Low hanging fruit: Much better natural features like grass, plants, trees, rocks, water other than sea level, etc.. Possibly applied via over-layered textures over the terrain map, as in Unity3D.<br />
* Save and load "region" content (Next Gen OAR) direct conversion to and from modern content creation tools like Unity and Blender.<br />
<br />
== Standards and Metaverse Standard Groups That Might Be Interesting ==<br />
<br />
* [https://vrm-consortium.org VRM Avatars], [https://github.com/vrm-c Github]: humanoid avatar format popular with vtubers, VRChat, etc<br />
* [http://infinitemetaverse.com/ Infinite Metaverse Alliance]<br />
* [https://omigroup.org/home/ Open Metaverse Interoperability Group]</div>Aiaustinhttp://opensimulator.org/wiki/Next_GenNext Gen2022-02-15T21:36:23Z<p>Aiaustin: /* Ai Austin Notes */ twi more</p>
<hr />
<div>A wiki area to discuss a possible next generation OpenSimulator.<br />
<br />
There are many people and groups who have forks and OpenSim side projects. <br />
MisterBlue on 14-Feb-2022 suggested puling these side projects together and<br />
make a new and improved future OpenSimulator. <br />
<br />
MisterBlue is creating a new protocol and viewer that initially works with OpenSim.<br />
A new viewer is a first step toward breaking away from the chains of Second Life<br />
compatibility.<br />
<br />
The list of requested features from attendees of [http://conference.opensimulator.org/2021/ OpenSimulator Community Conference 2021 (OSCC21)] is a good starting point.<br />
<br />
* [[OSCC21_Suggestions|OSCC21 Audience Feature Suggestions]]<br />
<br />
==MisterBlue Notes==<br />
<br />
Misterblue 2022-02-15: This effort is to invigorate and advance OpenSimulator for the most recent rediscovery of metaverses.<br />
The goal is not to re-engineer everything to create the ultimate metaverse.<br />
The goal is to address large features that are limiting OpenSimulator's deployment and use.<br />
In the end, there will be a divergence from complete mimicking of SL.<br />
<br />
There will be incorporation of technologies like:<br />
<br />
* popular asset creation tools (mesh editors, UI designers, etc.),<br />
* standards (identity, avatar formats, asset formats, etc.),<br />
* modern infrastructures (containers, monitors, etc),<br />
* encryption (end-to-end encrypted chat, signed storage, etc), and<br />
* storage systems.<br />
<br />
That's all grand vision, but on more practical grounds, the OSCC21 feature suggestions break into:<br />
<br />
* Use of popular asset formats for world and avatars (FBX, GLTF, VRM, etc)<br />
* Use of popular external asset creation tools (Blender, etc)<br />
* Updated user documentation<br />
* Improved in-world asset representations (trees, PBM materials, etc)<br />
* Incorporation of hyped metaverse tech (VR, text-to-speech, etc)<br />
<br />
And add to these operational features:<br />
<br />
* Easy to install and operate (db management, etc)<br />
* Better access control (connection control, user management, etc)<br />
* Security (for inter-grid and inter-user communication)<br />
<br />
==Ai Austin Notes==<br />
<br />
AiAustin 2922-02-15<br />
* To initially stay compatible with the current OpenSim platform, users and content would be helpful. Starting with an existing eco-system helps establish usage.<br />
* Suggest a strongly modular approach that allows for very different modules on different "regions" so that new forms of support can be provided on these, with fall backs when they are not available.<br />
* Low hanging fruit: Much better natural features lije grass, plants, trees, rocks, water other than sea level. Possibly applied via over-layered textures over the terrain map, as in Unity3D.<br />
* Save and load OAR direct conversion to and from modern content creation tools like Unity and Blender.<br />
<br />
== Standards and Metaverse Standard Groups That Might Be Interesting ==<br />
<br />
* [https://vrm-consortium.org VRM Avatars], [https://github.com/vrm-c Github]: humanoid avatar format popular with vtubers, VRChat, etc<br />
* [http://infinitemetaverse.com/ Infinite Metaverse Alliance]<br />
* [https://omigroup.org/home/ Open Metaverse Interoperability Group]</div>Aiaustinhttp://opensimulator.org/wiki/Next_GenNext Gen2022-02-15T21:31:52Z<p>Aiaustin: /* MisterBlue Notes */ Date firnat suggestion</p>
<hr />
<div>A wiki area to discuss a possible next generation OpenSimulator.<br />
<br />
There are many people and groups who have forks and OpenSim side projects. <br />
MisterBlue on 14-Feb-2022 suggested puling these side projects together and<br />
make a new and improved future OpenSimulator. <br />
<br />
MisterBlue is creating a new protocol and viewer that initially works with OpenSim.<br />
A new viewer is a first step toward breaking away from the chains of Second Life<br />
compatibility.<br />
<br />
The list of requested features from attendees of [http://conference.opensimulator.org/2021/ OpenSimulator Community Conference 2021 (OSCC21)] is a good starting point.<br />
<br />
* [[OSCC21_Suggestions|OSCC21 Audience Feature Suggestions]]<br />
<br />
==MisterBlue Notes==<br />
<br />
Misterblue 2022-02-15: This effort is to invigorate and advance OpenSimulator for the most recent rediscovery of metaverses.<br />
The goal is not to re-engineer everything to create the ultimate metaverse.<br />
The goal is to address large features that are limiting OpenSimulator's deployment and use.<br />
In the end, there will be a divergence from complete mimicking of SL.<br />
<br />
There will be incorporation of technologies like:<br />
<br />
* popular asset creation tools (mesh editors, UI designers, etc.),<br />
* standards (identity, avatar formats, asset formats, etc.),<br />
* modern infrastructures (containers, monitors, etc),<br />
* encryption (end-to-end encrypted chat, signed storage, etc), and<br />
* storage systems.<br />
<br />
That's all grand vision, but on more practical grounds, the OSCC21 feature suggestions break into:<br />
<br />
* Use of popular asset formats for world and avatars (FBX, GLTF, VRM, etc)<br />
* Use of popular external asset creation tools (Blender, etc)<br />
* Updated user documentation<br />
* Improved in-world asset representations (trees, PBM materials, etc)<br />
* Incorporation of hyped metaverse tech (VR, text-to-speech, etc)<br />
<br />
And add to these operational features:<br />
<br />
* Easy to install and operate (db management, etc)<br />
* Better access control (connection control, user management, etc)<br />
* Security (for inter-grid and inter-user communication)<br />
<br />
==Ai Austin Notes==<br />
<br />
AiAustin 2922-02-15<br />
* To initially stay compatible with the current OpenSim platform, users and content would be helpful. Starting with an existing eco-system helps establish usage.<br />
* Suggest a strongly modular approach that allows for very different modules on different "regions" so that new forms of support can be provided on these, with fall backs when they are not available.<br />
<br />
== Standards and Metaverse Standard Groups That Might Be Interesting ==<br />
<br />
* [https://vrm-consortium.org VRM Avatars], [https://github.com/vrm-c Github]: humanoid avatar format popular with vtubers, VRChat, etc<br />
* [http://infinitemetaverse.com/ Infinite Metaverse Alliance]<br />
* [https://omigroup.org/home/ Open Metaverse Interoperability Group]</div>Aiaustin