404
Page not found :(
The requested page could not be found.
diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..ea58058 --- /dev/null +++ b/404.html @@ -0,0 +1 @@ +
Page not found :(
The requested page could not be found.
1
+
...
+
1
+
...
+
1
+
...
+
1
+
...
+
1
+
...
+
1
+
...
+
1
+
...
+
1
+
...
+
1
+
...
+
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
Examples of behavior that contributes to creating a positive environment include:
Examples of unacceptable behavior by participants include:
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at nevyn@alloverse.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership.
This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq
This is an Elixir process that acts very similarly to a dedicated game server. You run it, and it acts as a container/world simulator for all users and apps that connect to it.
There is an always-on development server at alloplace://nevyn.places.alloverse.com. You can use it to try out your code so you don’t have to run a server locally if all you’re interested in is client development.
If you DO want to do server development, see the setup instructions in alloplace’s README for setup and run instructions.
Once it’s running, you have a server on alloplace://localhost!
This is the “game client” that connects to the alloplace server.
See its build instructions in its README
It uses CI integration to get the latest version of “allonet”, the network library used to talk to the Alloverse. It’s all in the README how you set that up.
See the appliance dev guide.
Allonet is the network layer. Unless you’re doing deep protocol hacking, you shouldn’t need to mess with it. It’s built into allovisor and alloplace, and should work as-is.
If you want to hack on it…
Allonet is written in C and CMake. I recommend using Visual Studio Code, and installing the “CMake-Tools” package to build and run from the GUI.
You can then either run ./build/allodummyclient
to try things out. If you want to try it in the real Allovisor, copy liballonet.dylib
into the Unity project’s assets, something like this:
cp build/liballonet.dylib ../allovisor/Assets/liballonet.bundle
+
For an overview of what a component is, please see Terminology.
These components are defined by placeserv. Third party developers may create any components they want. They can vote to make their own components official by pinging the dev team on Discord.
Defines the physical location and orientation of the entity relative to its parent entity if it has one; otherwise relative to the world origo. See Coordinate System for an extended description of how things are positioned and oriented in Alloverse.
"transform": {
+ "matrix": [1.0,0.0,0.0,0.0, 0.0,1.0,0.0,0.0, 0.0,0.0,1.0,0.0, 0.0,0.0,0.0,1.0]
+}
+
note: In an early version of the protocol, transform was represented as a 3-element position vector and 3-element rotation vector with euler angle rotations.
Defines the visual geometry of an entity. This should use assets in the future, but until we have assets, geometry is encoded in-line in entity description.
If type is asset
, you are providing an asset identifier that is a hash of the contents of the asset.
You also need to implement the client asset callbacks in order to respond to asset requests in order to deliver the asset.
"geometry": {
+ "type": "asset",
+ "name": "asset:sha256:d2a84f4b8b650937ec8f73cd8be2c74add5a911ba64df27458ed8229da804a26"
+}
+
If type is hardcoded-model
, you’re using one of the models hard-coded into the visor. name
is the name of the model.
"geometry": {
+ "type": "hardcoded-model",
+ "name": "hand"
+}
+
If type is inline
, Well.. you’re living in the past This is only recommended for debugging, and until we have geometry assets.
vertices
is a required list of lists, each sub-list containing x, y, z coordinates for your vertices.normals
is an optional list of lists, each sub-list containing the x, y, z coords for the normal at the corresponding vertex.uvs
is an optional list of lists, each sub-list containing the u, v texture coordinates at the corresponding vertex.triangles
is a required list of lists. Each sub-list is three integers, which are indices into the above arrays, forming a triangle."geometry": {
+ "type": "inline",
+ "vertices": [[1.0, 1.0, 1.0], [2.0, 2.0, 2.0], [3.0, 3.0, 3.0], [4.0, 4.0, 4.0]],
+ "normals": [[1.0, 1.0, 1.0], [2.0, 2.0, 2.0], [3.0, 3.0, 3.0], [4.0, 4.0, 4.0]],
+ "uvs": [[0.0, 0.0], [0.5, 1.0], [1.0, 0.0], [1.0, 1.0]],
+ "triangles": [[0, 1, 2], [1, 2, 3]]
+}
+
geometry
used to also contain texture
, but that’s been moved to material.texture
.
Defines the surface appearance of the component being rendered.
"material": {
+ "color": [1.0, 1.0, 0.0, 1.0],
+ "shader_name": "plain",
+ "texture": "asset:sha256:d2a84f4b8b650937ec8f73cd8be2c74add5a911ba64df27458ed8229da804a26"
+}
+
color
: An array of R, G, B and A value to set as base color for the thing being rendered. Default is white.shader_name
: Optional ame of hard-coded shader to use for this object. Currently allows plain
and pbr
. Default is plain
.texture
: optional texture asset. Default is none.Configures poses for bone nodes in a rigged mesh. Use this to manually pose/animate the skeleton/bones of an entity that has a rigged model as its geometry
.
"skeleton": {
+ "nodes": {
+ "{name of node}": { // key is name of node
+ "matrix": [1,0,0,0, 0,1,0,0, ...]],
+ "alpha": 1.0,
+ },
+ }
+}
+
nodes
: A dictionary of nodes to pose. The key is the name of the node.The structure of each node dictionary:
matrix
: a 4x4 matrix like in transform
used to position and rotate the nodealpha
: Mix this matrix with the node’s default pose. 0.5 means using the half-way point between the two transforms, 1.0 means using only the incoming transform.Defines a text renderer for this entity, drawing a text texture at transform
.
"text": {
+ "string": "hello world",
+ "height": 0.03,
+ "wrap": 0,
+ "halign": "center"
+}
+
height
: The height of the text. 1 unit = 1 meter.wrap
: The width in meters at which to wrap the text, if at all.halign
: Horizontal alignment; “center”, “left” or “right”.Defines the physical shape of an entity.
"collider": {
+ "type": "box",
+ "width": 1,
+ "height": 1,
+ "depth": 1
+}
+
Specify the relationships between entities, in particular child entites’ “parent” entity. If an entity has a parent, its transform should be concatenated with all its ancestors’ transforms before being displayed.
"relationships": {
+ "parent": "abc123"
+}
+
Specify how the entity’s owning agent’s intent affects this entity.
actuate_pose
: this named pose will be set as this entity’s transform each frame.from_avatar
(optional): Instead of following the owning agent’s intents, follow the agent who has this entity as its avatar."intent": {
+ "actuate_pose": "hand/left",
+ "from_avatar": "abc123"
+}
+
Describes how an entity maybe grabbed/held, and then moved/dragged by a user.
The actual grabbing is accomplished using intents. See the field grab
under intent.
"grabbable": {
+ "actuate_on": "...",
+
+ "translation_constraint": [1, 1, 1],
+ "rotation_constraint": [1, 1, 1],
+ "target_hand_transform": [1, 0, 0, 0, 0, 1, .....],
+}
+
actuate_on
: Since the grabbable
component is likely attached to a a handle rather than the entire object being movable, actuate_on indicates how far up this entity’s ancestry to walk before deciding which entity to actually move. $parent
means move the parent entitytranslation_constraint
: Only allow the indicated fraction of movement in the corresponding axis in the actuated entity’s local coordinate space. E g, to only allow movement along the floor (no lifting), set the y fraction to 0: `“translation_constraint”: [1, 0, 1]”.rotation_constraint
: Similarly, constrain rotation to the given fraction in the given euler axis in the actuated entity’s local coordinate space. E g, to only allow rotation along Y (so that it always stays up-right), use: "rotation_constraint": [0, 1, 0]
.target_hand_transform
: If omitted, the relationship between the hand and the target object is kept constant throughout the grab. If set, the relationship between the hand and the target is set to this 4x4 transformation matrix upon grabbing it. For example, set this to the identity matrix to make an object move immediately into the user’s hand when grabbed.The entity that holds a live_media
component for a specific track is the entity that “plays” that track; e g for audio, audio will be played from the location of that entity.
Please do not try to create live-media components manually. They must be allocated server-side so that the server can allocate a track stream in the network protocol. Instead, send allocate_track to place
to add a live_media
component to your entity.
track_id
: CHANNEL_MEDIA
track number that corresponds to what this entity should play backtype
: audio
or video
format
: what media format encoder/decoder to use.metadata
: a dict of type+format specific metadata about the format of the data.For audio, the only valid format is opus
. The valid metadata
fields for opus audio are:
sample_rate
: playback sample ratechannel_layout
: “mono” supported for now.For video, the two valid formats are mjpeg
and h264
.
mjpeg
for video where a low framerate is okay but you want to make sure everybody always has the latest frame, such as for a whiteboard or drawing app.h264
for high framerate video, or decorative video, such as camera, screenshare or a video frame.The valid metadata
fields for video are:
width
: Width in pixels of each video frameheight
: Height in pixels of each video frame"live_media": {
+ "track_id": 0, // filled in by server
+ "type": "audio",
+ "format": "opus"
+ "metadata": {
+ "sample_rate": 48000,
+ "channel_count": 1,
+ }
+}
+
Legacy format:
"live_media": {
+ "track_id": 0, // filled in by server
+ "sample_rate": 48000,
+ "channel_count": 1,
+ "format": "opus"
+}
+
Play a sound emanating from this entity, based on a sound asset.
Supported file formats:
Visors will load the sound asset upon encountering it. It won’t play until the starts_at
field is set and server clock reaches that time.
It is recommended that you first create and publish this entity without the starts_at
field so that visors cache it, and only later sets a starts_at time so that it’s more likely to be loaded by the time it’s played.
asset
: ID of the asset to play. (required)starts_at
: The server clock time at which to start playing. Set to nil to just load but not play at this time. (optional, default nil)loop_count
: How many times should it loop? 0 means no looping (optional, default 0).offset
: Skip n
seconds of audio into the file. (optional, default 0, not implemented yet)length
: Play only n
seconds of audio from the file, skipping the rest of the file before ending or looping. (optional, default false, not implemented yet)volume
: Playback volume (optional, default full volume 1.0)finish_if_orphaned
: Keep playing the sound to finish even if the component or entity is removed"sound-effect": {
+ "asset": "asset:sha256:blabla",
+ "starts_at": 12345.005,
+ "loop_count": 2,
+ "offset": 3.0,
+ "length": 5.56,
+ "volume": 1.0,
+}
+
Only set on the entity place
, this component defines the flow of time for a place.
"clock": {
+ "time": 123.0, // in seconds
+}
+
Its reference time is undefined. It is always seconds as a double.
Defines a custom cursor renderer, controlling the appearence of the cursor displayed when pointing at the entity.
"cursor": {
+ "name": "brushCursor",
+ "size": 3,
+}
+
name
: The name of the custom cursor. There’s currently only one defined; “brushCursor”, which displays a white circle.size
: The brushCursor’s radius, meant to match the size of the current brush size when interacting with a drawable surface. 1 unit = 1 centimeter. Default: “3”.A list of property animations to play.
"property_animations": {
+ "animations": {
+ "abc123": { ... }
+ }
+}
+
animations
contains key-value pairs of animation IDs and animation descriptors. You don’t manually create this component; instead, please use these interactions:
… to modify the list of animations. Note that animations are automatically removed if they’re non-repeating and their progress reaches 100%.
Each animation descriptor has the following structure:
{
+ "path": "transform.matrix.rotation.y",
+ "from": 0,
+ "to": 3.14,
+ "start_at": 10004.2,
+ "duration": 0.5,
+ "easing": "quadInOut",
+ "repeats": true,
+ "autoreverses": true
+}
+
path
: You describe the property to be animated by setting the path to the key path of the property For example, to change the alpha field (fourth field) of the color property of the material
component, use the path material.color.3
(0-indexed). Required rotation
, scale
and translation
of a matrix to directly set that attribute of the matrix.transform.matrix.rotation.y
. In that case, the “from” and “to” values can be regular numbers.from
: The value to animate from. Can be a number, matrix (list of 16 numbers), vector (list of 3 numbers) or rotation (list of 4 numbers: angle, and the x y z of the axis). It MUST be the same kind of value as the property we’re animating. Required.to
: The value to animate to. See from
. Required.start_at
: The server time at which to start the animation. Required.duration
: Duration, in seconds. Required.easing
: Easing algorithm. Default linear
. Allowed values: linear
, quadInOut
, quadIn
, quadOut
, .bounceInOut
, bounceIn
, bounceOut
, backInOut
, backIn
, backOut
, sineInOut
, sineIn
, sineOut
, .cubicInOut
, cubicIn
, cubicOut
, quartInOut
, quartIn
, quartOut
, quintInOut
, quintIn
, .quintOut
, elasticInOut
, elasticIn
, elasticOut
, circularInOut
, circularIn
, circularOut
, .expInOut
, expIn
, expOut
.repeats
: Whether to play again from the start after animation finishes. Default false
.autoreverses
: Whether every other repeated iteration should be in reverse. Default false.
service_discovery
Indicates that the entity provides some sort of service as an interaction API.
{
+ "name": "servicename",
+ "description": "Short developer-readable description of the service",
+ "docs": "https://url/to/documentation"
+}
+
For example, to find a service that lets you view image files, you might look for a service name imageviewer
, and look into its documentation on how to ask it to display a specific file.
This document explains the various concepts. For exact protocol specifications, see the Protocol Reference.
A place, a user/visor and appliances are all actors using the same network protocol and RPC mechanics, implemented in the allonet
library.
Streams:
Allonet multiplexes these over UDP using enet
. The plan is to wrap this in TLS.
An alternative plan has been to use WebRTC as the transport for free TLS, ICE, udp punch-through, etc. We might still do that in the future, if a nice open source library appears.
Entities are things in the world, represented in a scene graph. An entity has only an “id” and a list of components. There is a list of specifications for official components. Apps may invent additional components without them having to be part of the standard.
Interactions are the way entities can communicate, including how the visor talks to the place itself (by sending to entity id place
). It’s a json list for now, and the idea is for it to be a pub-sub request-response protocol vaguley inspired by Peter J Robinson et al’s Pedro protocol.
The documentation has a list of specifications for official interactions, but again, apps and visors can invent their own beyond the specification.
The alloplace server becomes the Pedro gateway, and can thus manage access control lists, defined as guard expressions on the expression itself, the sender and the receiver. See the implementation task for more information.
These ACLs can then control everything needed:
This should be exposed in the visor as much more user friendly than the bullet list above makes it seem…
The alloplace server holds all the state and takes care of all the logic. It is the world state, RPC and media transit hub.
I’d love to support different visors: VR, AR desktop 3D and touch. For now, the focus is on the VR visor in Lovr.
alloplace://(enet endpoint)...
URLs can be opened to make a visor open a place.
A visor should provide some sort of client identity. The idea for now is for each client to provide a public certificate with a name bound to it, so you can at least know when the same client appears again. Would love an architecture idea for a distributed identity plan (web of trust?).
If we end up going with webrtc, the enet endpoint should be replaced with a https endpoint that takes a POST
of the SDP for an OFFER
, and responds with the ANSWER
.
Appliances are written in any language and interact with the allonet
library acting as a “UIKit” for XR UI.
While appliances can use the allonet API directly, it uses low-level primitives that are not great for building 3D UIs. The plan is to create an “Allo UI” UI kit API on top of allonet that spawns entities, shares textures etc as necessary to provide the UX and UI interactions that Alloverse standardizes on, providing an intuitive and uniform experience across appliances without every application developer having to think of their own interaction patterns.
If a user opens an alloapp+http(s)://...
, it should be opened by the visor. The visor should then send an interaction to the alloplace server, asking it to spawn the given appliance.
The alloplace server should then make the http(s) call to the given URL with a connection instruction json blob:
{
+ "spawnInPlace": "alloplace://hostname:port",
+ "onBehalfOf": (client identity),
+ "query": (query params as-is from the alloapp url)
+ }.
+
The http server receiving this request should then spawn a process for this agent and have it connect to the spawnInPlace
URL. See the implementation task for opening appliances with URLs.
The plan is to provide a generic “alloapp gateway” node script that responds to requests like the one above which can be easily configured to spawn a process like the above, so that all an app developer needs to implement is a process that takes a place URL in ENV (or the whole json as above), and then asks the allonet API to connect to that URL.
Entities should have components representing assets such as:
They should reference these assets by sha1 hash. When a alloplace server sees a new hash in the description of an entity coming from an agent, it should ask that agent for that asset hash over the asset channel. The agent should then stream the asset to the server for caching and redelivery.
When another agent then sees the references to the assets, it can thus then ask the alloplace for the corresponding asset, so that it can then later display it.
Dynamic textures and audio will later be represented as webrtc streams, and thus not considered assets the same way. Placeserv then acts as an SFU.
For an overview of what Alloverse and alloapps are, please see the first chapters of the Get Started (Lua) guide.
Note that Lua is the preferred alloapp language, and C# language support is in beta.
To write Alloverse apps in C#, you’ll need the following:
Personally, I’m more of a Linux/Mac with command line kind of person, so this guide will primarily be using the dotnet
. I’ll try to include the equivalent Visual Studio screenshots so that you can follow along without using a Terminal.
Let’s create a new C# project:
Add AlloUI as a dependency:
You remember how I said at the top that this is a beta? There’s one really terrible hack you’ll have to do until I figure out how to properly package native code in a nuget package.
You have to manually download liballonet.so
, and put it at /home/nevyn/Dev/allonet/build/liballonet.so
. This unfortunately means that this’ll only work on Linux for now.
Open up Program.cs
. The most basic setup for an alloapp is one which:
Modify your file to look something like this, adjusting to taste;
That’s it! You should now be able to start your project and see it appear in an Alloplace. If you don’t have one already, you can use our sandbox alloplace://sandbox.places.alloverse.com, or be ambitious and boot one on your own (docker run -e ALLOPLACE_NAME="my place" -p 21337:21337/udp -it alloverse/alloplace2
). I’m gonna do the former:
If it all checks out, you should be able to jump into that place on your VR headset or computer and press the button it has created.
Download the Visor app for your VR or desktop platform of choice, and then click “Connect”, “Sandbox” (or click the alloplace link of the place you’ve rented or started yourself).
Our app running in Sandbox place. I’ve clicked our simple app’s white button button three times, and the laws of causality held up, printing “Hello” thrice to our terminal.
At this point, you could go off, do your own thing, read the documentation as you go. Or you could keep reading, and be guided through the development of a simple but functional todo list app, which will help establish the fundamentals and make it much easier for you to build real, complex apps.
This is also a good point at which to remind you: if you get stuck, or have questions or feedback, please hop on Discord and give it to us straight.
This is where one would expect the tutorial to really get started, teaching the deeper concepts and doing something more advanced. But alas, the number of hours in a day are finite, and the docs are left unwritten.
Maybe see it as a challenge? “Left as an exercise to the reader” and all that. Who knows, maybe you end up sending us a docs improvement PR? ;)
And again, if you have any questions, hop on Discord!
Represents the AlloApp.
Mediates communication with backend, and maintains the runloop. Create one of these, configure it, connect it and run it, and you have an alloapp.
local app = App(client)
+
Name | Type | Description |
---|---|---|
client | Client | The AlloNet client wrapper |
Nothing
Open a ui.View as a popup near a hand. Call from e g a button handler to display it right where the user could easily interact with it. You can use this to open a “new item” or a “settings” UI near a user. Since apps are multi-user, sometimes it’s nice to give a user some personal UI that they can do input in, so that they don’t have to fight over control of input with another user.
Name | Type | Description |
---|---|---|
popup | ui.View | The view to show to the user. It will be instantiated in |
the world at the appropriate location. | ||
hand | Entity | The hand entity that the popup should be shown near. |
In a button handler, this is the first argument to the callback. | ||
distance | number | The distance in meters from the hand to show. Default 0.6 |
entity | Callback(view, | ) cb The callback to call when the popup is present in-world |
Nothing
Add a widget to the user’s left wrist, and arrange it to fit with the other widgets already present. Use this to provide some portable UI to the user, such as a remote control.
| Name | Type | Description | | ———— | ————– | ————— | | avatarOrHand | Entity | The avatar of the user to add the widget to. Can also be any child entity to the avatar, such as a hand, and the avatar will be looked up for you. | | widget | ui.View | The widget to add. Must be at most 3cm wide. You can make it a button or something that opens more UI nearby if you need more space. | | callback | Callback(bool) | Callback for when adding widget finishes. Its argument is true if successful. |
Nothing
Schedule work to be done later
Name | Type | Description |
---|---|---|
delay | number | The time (in seconds) until the callback is called. |
repeats | boolean | Whether The callback should repeat (with the same delay) or only occur once. |
callback | function | The function to be called (with no arguments). |
Nothing
Current client time. This is monotonically increasing.
None
Nothing
Current server time. Might jump around a little to compensate for lag.
None
Nothing
Asks the backend to update components on the server.
Use this to update things you’ve specified in :specification() but now want to change.
Name | Type | Description |
---|---|---|
changes | table | A table with the desired changes, for example: {transform={…}, collider={…}} |
removals | table | A table with the keys of componets to remove, for example: {“collider”, “skeleton”} |
Nothing
Represents data that can be shared to other clients in the Alloverse
An asset is just raw data.
asset = Asset(data)
+
Name | Type | Description |
---|---|---|
data | string | Raw data for the asset. |
Nothing
Read a part of the data
Name | Type | Description |
---|---|---|
offset | number | The byte to start reading from |
length | number | The number of bytes to read |
Type | Description |
---|---|
string | the requested data |
Write a part of the data
Name | Type | Description |
---|---|---|
data | string | The data buffering |
offset | number | The byte offset to start writing at |
totalSize | number | The expected total size of the asset. |
Nothing
Returns the size of the asset
None
Type | Description |
---|---|
number | The size of the data |
Returns the pixel width and height of the asset that represents an image. Returns nil if the asset doesn’t represent an image.
None
Type | Description |
---|---|
(number, | number) The width and height of the image (in pixels) |
Returns a computed unique identifier for this asset The id is a hash of the asset data. This ensures the same asset identifier is always matched with the same data
Name | Type | Description |
---|---|---|
refresh | boolean | By default a cached hash is returned, if one is available. Send refresh to true to recompute the id |
Nothing
Returns a File-like wrapper to send to methods that wants files It’s not fully implemented; Fill out methods as needed.
None
Type | Description |
---|---|
FileWrapper | An object that implmeents the same methods as io.File |
A utility class for storage and access to Assets.
assetManager = AssetManager(client)
+
Name | Type | Description |
---|---|---|
client | Client | The allonet client to handle assets for |
Nothing
Adds an Asset to the AssetManager
AssetManager:add(asset, manage)
+
Name | Type | Description |
---|---|---|
asset | Asset | The Asset to add |
manage | bool | If set to true , the AssetManager will hold on to the asset for you. If set to false , it will only serve the asset as long as you keep a reference to it. |
Nothing
Removes an Asset from the AssetManager
AssetManager:remove(asset)
+
Name | Type | Description |
---|---|---|
asset | Asset | The Asset to remove |
Nothing
Get an Asset with the corresponding name. Returns nil
if no matching Asset is found.
AssetManager:get(name)
+
Name | Type | Description |
---|---|---|
name | string | The name of the requested string |
Type | Description |
---|---|
Asset | The corresponding Asset |
Gets all Assets currently held by the AssetManager.
AssetManager:all()
+
None
Type | Description |
---|---|
Table | All assets held by the AssetManager |
Get the number of Assets currently held by the AssetManager.
AssetManager:count()
+
None
Type | Description |
---|---|
number | The number of assets held by the AssetManager |
Start to load an asset. Callback will be called with the Asset when loading is complete, or with nil
if failed.
AssetManager:load(name, callback)
+
callback: function(name, asset_or_nil) returns true if the asset is loading. returns false if asset was found in cache
Name | Type | Description |
---|---|---|
name | ??? | |
callback | ??? |
Nothing
A component that defines the pose and size of a View, i.e. its bounds within the world. Note that any changes made to the Bounds using the methods below won’t be applied (and thus visible in the world) until View:setBounds() is been run.
bounds = Bounds(a, b, c, w, h, d)
+
For convenience, a Bounds may also be created using Pose and Size objects:
bounds = Bounds{pose=,size=}
+ bounds = Bounds(pose, size)
+
Name | Type | Description |
---|---|---|
a | number | The View’s x position in the world |
b | number | The View’s y position in the world |
c | number | The View’s z position in the world |
w | number | The View’s width |
h | number | The View’s height |
d | number | The View’s depth |
Nothing
Makes a copy of a given Bounds object
None
Type | Description |
---|---|
Bounds | A copy of the given Bounds. |
Sets the Bounds’ Pose component to {0, 0, 0}
None
Type | Description |
---|---|
Bounds | The updated Bounds object. |
Rotates the Bounds around an axis.
Name | Type | Description |
---|---|---|
angle | number | The amount to rotate the coordinate system by, in radians. |
x | number | The x component of the axis of rotation. |
y | number | The y component of the axis of rotation. |
z | number | The z component of the axis of rotation. |
Type | Description |
---|---|
Bounds | The updated Bounds object. |
Moves the Bounds. May be used in conjunction with Size:getEdge() to move the Bounds relative to its parent.
Name | Type | Description |
---|---|---|
x | number | The movement along the x axis. |
y | number | The movement along the y axis. |
z | number | The movement along the z axis. |
Type | Description |
---|---|
Bounds | The updated Bounds object. |
Scales the Bounds.
Name | Type | Description |
---|---|---|
x | number | The scaling along the x axis (or all axes, if y and z aren’t provided). |
y | number | The scaling along the y axis. |
z | number | The scaling along the z axis. |
Type | Description |
---|---|
Bounds | The updated Bounds object. |
Shrinks the Bounds by the given parameters.
Name | Type | Description |
---|---|---|
w | number | The reduction of the component’s width. |
h | number | The reduction of the component’s height. |
d | number | The reduction of the component’s depth. |
Type | Description |
---|---|
Bounds | The updated Bounds object. |
A button that can be poked/clicked to perform an action.
Every button has a Label. Use label:setText(…) to set it:
my_button.label:setText("this is my button")
+
Set onActivated
to a function you’d like to be called when the button is pressed:
my_button.onActivated = function()
+ -- do something...
+ end
+
You can also set the button’s default, highlighted and activated texture (see Surface documentation for image format caveats). Or if you just want a colored button, you can set its color. Set either color or texture to nil to remove that attribute.
my_button = Button(bounds)
+
Name | Type | Description |
---|---|---|
bounds | Bounds | The button’s initial bounds. |
text | string | The button’s initial text |
Nothing
Sets the texture of the button Set to nil to remove the attribute.
Name | Type | Description |
---|---|---|
asset | Asset | The texture asset |
Nothing
Sets the color of the button Set to nil to remove the attribute.
Name | Type | Description |
---|---|---|
rgba | table | A table with the desired color’s r, g, b and alpha values between 0-1, e.g. {0.8, 0.4, 0.8, 0.5} |
Nothing
???
client = Client(url, name, client, updateStateAutomatically)
+
Name | Type | Description |
---|---|---|
url | string | … |
name | string | … |
client | Client | The AlloNet client. |
updateStateAutomatically | boolean | Whether or not the client should automatically update its state. |
Nothing
Fetch an entity when it is received This is asynchronous because you might receive an interaction with an entity ID pointing to an entity that you haven’t received yet.
Name | Type | Description |
---|---|---|
function | cb | (entity) callback when entity is arrived |
Nothing
Ask the place to launch an app found at appurl
.
| Name | Type | Description | | ——– | —— | ————— | | string | appurl | The alloapp:
URL to launch into the connected place | | Pose | pose | Transform/position of where in the place to launch the app | | table | args | key-value table of additional arguments to send to the app | | function | cb | Callback for how the launch went. On error, called as cb(false, errstr)
. On success, called as cb(true, launchedAvatarId)
. |
Nothing
Send an RPC message (aka “interaction”) to another entity.
If you’re sending a “request” interaction (default), you should really listen to the callback to make sure your call succeeded.
Name | Type | Description |
---|---|---|
Interaction | interaction | a populated Interaction struct |
Function | callback | (interaction, body) a callback that takes the response interaction and the parsed response body. |
Nothing
Send and receive buffered data synchronously now. Loops over all queued network messages until the queue is empty.
Name | Type | Description |
---|---|---|
timeout_ms | ??? | how many ms to wait for incoming messages before giving up. Default 10. |
Type | Description |
---|---|
bool whether any messages were parsed |
A simple cube. Commonly used as a placeholder asset or marker.
cube = Cube(bounds)
+
Name | Type | Description |
---|---|---|
bounds | Bounds | The Cube’s initial bounds. |
Nothing
Sets the Cube’s color
Name | Type | Description |
---|---|---|
rgba | table | The r, g, b and a values of the text color, each defined between 0 and 1. For example, {1, 0.5, 0, 1} |
Nothing
An entity is the manifestation of an agent in a place.
The only fixed information in an entity is its id. Everything else is specified as a set of components.
Gets the parent of a given Entity.
None
Type | Description |
---|---|
Entity | The parent Entity, or nil if it has no parent. |
Gets the topmost parent of a given Entity. E g, if given a user’s hand, gets the root avatar for that user.
None
Nothing
Finds the first descendant of self where calling predicate with each descendant returns true.
Name | Type | Description |
---|---|---|
The | Callback(entity):Bool | predicate callback to run for each descendant |
Type | Description | |
---|---|---|
Entity | nil | the found entity, or nil if non found |
A type of Asset generated from a file
my_asset = FileAsset(path, load)
+
Name | Type | Description |
---|---|---|
path | string | The path to the file |
load | boolean | If true , the file is loaded into memory immediately. Otherwise the file is read whenever data is needed. |
Type | Description |
---|---|
FileAsset | The generated FileAsset. |
Get the path to a FileAsset
path = FileAsset:path()
+
None
Type | Description |
---|---|
string | The path to the FileAsset. |
Get the size (in bytes) of a FileAsset.
path = FileAsset:size()
+
None
Type | Description |
---|---|
string | The size, in bytes, of the FileAsset. |
A FrameView is a View subclass that draws a border of a given width and color.
local frameview = FrameView(bounds, thickness)
+
For convenience, you may also set some or all of the FrameView’s properties within the constructor, i.e.:
local frameview = FrameView{bounds=Bounds(0, 0, 0, 1.0, 0.1, 0.001), color={1.0,0.2,0.4,1}, position="inside"}
+
Name | Type | Description |
---|---|---|
bounds | table | A table including at least a Bounds component. |
thickness | number | The thickness of the border. |
Nothing
A widget for moving something; think of it as the title bar of a window.
Grabbing the title bar moves the window, not the title bar. Same here; set this as the subview of your root view to make it movable. You can set any bounds on this handle to position it at a good location in your view.
grab_handle = GrabHandle(bounds)
+
Name | Type | Description |
---|---|---|
bounds | Bounds | The GrabHandle’s bounds. |
Nothing
A label, used to display text in Alloverse
--Creates a Label, at origo, that is 1m wide, 20cm tall and 1cm deep
+ local l = Label{bounds=Bounds(0, 0, 0, 1, 0.2, 0.01)}
+
+ --For convenience, you may also set some or all of the Label's properties within the constructor, i.e.:
+ local l = Label{bounds=Bounds(0, 0, 0, 1.0, 0.1, 0.001), color={1.0,0.2,0.4,1}, text="Hello!", halign="left"}
+
Note: The font size of the text is, by default, derived from the height of the Label.
Name | Type | Description |
---|---|---|
o | table | A table including at least a Bounds component. It may also include a number of other optional properties: text, wrap, halign, color and fitToWidth. |
Nothing
Sets the Label’s text
Name | Type | Description |
---|---|---|
text | string | The text the Label should display |
Nothing
Sets the Label’s horizontal wrap attribute
Name | Type | Description |
---|---|---|
wrap | boolean | Whether the Label should line break when reaching the Label’s bounds’ (true ) or be allowed to overflow outside the component (false ) |
Nothing
Sets the Label’s horizontal align attribute
Name | Type | Description |
---|---|---|
halign | “center”,”top”,”bottom” | The alignment of the text within the Labels’ bounds |
Nothing
Sets the Label’s text color
Name | Type | Description |
---|---|---|
color | {r,g,b,a} | The r, g, b and a values of the text color, each defined between 0 and 1. |
Nothing
Sets the Label’s fitToWidth attribute Note: If true, the wrap
attribute is made irrelevant.
Name | Type | Description |
---|---|---|
fitToWidth | boolean | Wether the Label’s font size should dynamically shink in order to always fit in the label’s width. |
Nothing
A type of Asset generated from a file
my_asset = LovrFileAsset(path, load)
+
Name | Type | Description |
---|---|---|
path | string | The path to the file |
Type | Description |
---|---|
LovrFileAsset | The generated LovrFileAsset |
Get the path to a LovrFileAsset
path = LovrFileAsset:path()
+
None
Type | Description |
---|---|
string | The path to the LovrFileAsset |
Get the size of a LovrFileAsset
path = LovrFileAsset:size()
+
None
Type | Description |
---|---|
string | The size, in bytes, of the LovrFileAsset |
Navigation stack: pushes and pops views to drill down a hierarchy of UI.
Includes a back button to allow navigation.
navStack = NavStack(bounds)
+
Name | Type | Description |
---|---|---|
bounds | Bounds | The NavStack’s bounds. |
Nothing
Returns the item at the bottom of the stack.
Does not remove the item from the stack.
None
Type | Description |
---|---|
View | The item from the bottom of the stack |
Returns the item at the top of the stack.
Does not remove the item from the stack.
None
Type | Description |
---|---|
View | The item from the top of the stack |
Adds an item to the top of the stack
Name | Type | Description |
---|---|---|
view | View | The view to push |
Nothing
Returns the item from the top of the stack.
Removes the item from the stack.
None
Type | Description |
---|---|
View | top The item at the top of the stack |
A ninepatch is a View subclass which displays a 9patch texture on a square.
surface = Surface(bounds, texture, inset)
+
Name | Type | Description |
---|---|---|
bounds | Bounds | The Surface’s bounds. |
inset | number | The size of the border of the surface |
An | Texture | asset with the 9patch image texture to use |
textureInset | number | The number of pixels to inset from each side in the texture image |
Nothing
The “position” of a view. Abstracts a mat4 transformation matrix.
pose = Pose() -- Creates a zero pose
+
A Pose may also be created the following ways:
pose = Pose(transform) -- Creates a pose from a transform
+ pose = Pose(x, y, z) -- Create positioned pose
+ pose = Pose(a, x, y, z) -- Create rotated pose
+
Name | Type | Description |
---|---|---|
a | ??? | |
b | ??? | |
c | ??? | |
d | ??? |
Nothing
Creates and returns a copy of a given Pose
None
Type | Description |
---|---|
Pose | A copy of the original Pose object. |
Sets the Pose to {0, 0, 0, 0}.
None
Type | Description |
---|---|
Pose | original Pose, post reset to the identity matrix. |
Sets the Pose to inherit
Name | Type | Description |
---|---|---|
other | Pose | The source Pose to copy. |
Type | Description |
---|---|
Pose | the original Pose, after having been updated. |
Rotates the Pose
Name | Type | Description |
---|---|---|
angle | number | The angle component of the angle/axis rotation (radians). |
x | number | The x component of the axis of rotation. |
y | number | The y component of the axis of rotation. |
z | number | The z component of the axis of rotation. |
Type | Description |
---|---|
Pose | The original Pose, post-rotation. |
Moves the Pose
Name | Type | Description |
---|---|---|
x | number | The movement along the x axis. |
y | number | The movement along the y axis. |
z | number | The movement along the z axis. |
Type | Description |
---|---|
Pose | The original Pose, post-move. |
Scales the Pose
Name | Type | Description |
---|---|---|
x | number | The x component of the scale to apply (or all axes, if y and z aren’t provided). |
y | number | The y component of the scale to apply. |
z | number | The z component of the scale to apply. |
Type | Description |
---|---|
Pose | The original Pose, post-scale. |
Concatenates (multiplies) this transform with another
Name | Type | Description | |
---|---|---|---|
otherPoseOrTransform | Pose | mat4 | Either a CPML mat4 matrix to concatenate with, or another Pose |
Type | Description |
---|---|
Pose | The original Pose, post-concatenation. |
PropertyAnimation describes the animation of a field in a component in an entity.
It can be any field that is numeric, 4x4 matrix, vec3 or a rotation (as an angle-axis as four numbers).
Use View:addPropertyAnimation to apply and activate the animation.
Example usage:
self.logo:addPropertyAnimation(ui.PropertyAnimation{
+ path= "transform.matrix.rotation.y",
+ start_at = self.app:serverTime() + 1.0,
+ from= -0.2,
+ to= 0.2,
+ duration = 2.0,
+ repeats= true,
+ autoreverses= true,
+ easing= "elasticOut",
+ })
+
You describe the property to be animated by setting the path to the key path of the property.
For example, to change the alpha field (fourth field) of the color property of the material
component, use the path material.color.3
(0-indexed).
Matrices also have some magical computed properties. You can access rotation
, scale
and translation
of a matrix to directly set that attribute of the matrix. You can also dive into the specific setting for the x, y or z axies of each of those. For example, to rotate around y, you can animate transform.matrix.rotation.y
. In that case, the “from” and “to” values can be regular numbers.
Name | Type | Description |
---|---|---|
path | string | The keypath to the property to animate |
Nothing
The value to animate from. Can be a number, matrix (list of 16 numbers), vector (list of 3 numbers) or rotation (list of 4 numbers: angle, and the x y z of the axis). It MUST be the same kind of value as the property we’re animating (see setPath).
Name | Type | Description | |||
---|---|---|---|---|---|
from | number | mat4 | vec3 | {a,ax,ay,az} | Value to animate from |
Nothing
The value to animate to. See setFrom
.
Name | Type | Description | |||
---|---|---|---|---|---|
to | number | mat4 | vec3 | {a,ax,ay,az} | Value to animate to |
Nothing
The time at which to start the animation. Use App:serverTime() to get the current time, and use offsets from that time to get time in the future. To start an animation in four seconds, use myview.app:serverTime()+4
.
Name | Type | Description |
---|---|---|
start_at | number | Server time at which to start the animation |
Nothing
The number of seconds to animate for, in seconds.
After the time start_at + duration has elapsed, the animation is automatically removed, unless it’s a repeating animation.
Name | Type | Description |
---|---|---|
duration | number | Duration of animation in seconds |
Nothing
Set the easing curve used to animate along. The default is linear
, which means going in a straight line from from
to to
. This usually looks very stiff and robotic, and is discouraged. Use one of these easing algorithms instead:
Name | Type | Description |
---|---|---|
easing | string | Name of the easing algorithm to use |
Nothing
Whether this animation restarts plays again immediately after finishing.
Repeating animations are never removed automatically.
Name | Type | Description |
---|---|---|
repeats | bool | Whether to repeat |
Nothing
For repeating animations: Whether to play this animation back in reverse after each time it has been played front-to-back.
Name | Type | Description |
---|---|---|
autoreverses | bool | Whether to autoreverse |
Nothing
Asks server to stop the animation and remove it
Name | Type | Description |
---|---|---|
callback | ??? |
Nothing
Proxy icon: A grabbable 3d object that creates a copy that can be placed in the world.
Useful as a way to instantiate objects in the world from a palette. Subclass it and override onIconDropped().
icon = ProxyIconView(bounds)
+
Name | Type | Description |
---|---|---|
bounds | Bounds | The icon’s bounds. |
name | String | The asset’s name |
author | String | The asset’s author |
icon | FileAsset | asset to be used as the icon for this proxy view |
Nothing
Override this to handle the icon being dropped. You can also set it as a property on the instantiated icon view.
Name | Type | Description |
---|---|---|
at_transform | ??? |
Nothing
A window resize widget.
Grab and move to resize your view from its center point.
resizeHandle = ResizeHandle(bounds, translationConstraint, rotationConstraint)
+
Name | Type | Description |
---|---|---|
bounds | Bounds | The ResizeHandle’s bounds. |
translationConstraint | table | Only allow the indicated fraction of movement in the corresponding axis in the actuated entity’s local coordinate space. E g, to only allow movement along the floor (no lifting), set the y fraction to 0: {1, 0, 1} . |
rotationConstraint | table | Similarly, constrain rotation to the given fraction in the given euler axis in the actuated entity’s local coordinate space. E g, to only allow rotation along Y (so that it always stays up-right), use: {0, 1, 0} . |
Nothing
The setfenv(…) (in meters) of a view or element in the world.
Note that size and scale are different things: size is the semantic size of your app (e g “this button is 2 dm wide”) while scale is the scaling up or down of this semantic size (e g a transformation matrix with a 2.0 X-axis scale and a 2dm width is 4dm wide, but its content is still 2dm wide.)
size = Size(width, height, depth)
+
Name | Type | Description |
---|---|---|
width | number | The width of the component. |
height | number | The height of the component. |
depth | number | The depth of the component. |
Nothing
Creates and returns a copy of a given Size.
None
Type | Description |
---|---|
Size | A copy of the original Size object. |
Shrinks the Size component by the given parameters.
Name | Type | Description |
---|---|---|
byWidth | number | The x component of the size reduction. |
byHeight | number | The y component of the size reduction. |
byDepth | number | The z component of the size reduction. |
Type | Description |
---|---|
Size | The original Size object, post-resize. |
Sets width, height and depth
Name | Type | Description |
---|---|---|
width | number | The width of the component. |
height | number | The height of the component. |
depth | number | The depth of the component. |
Nothing
Returns the position relative to the object’s edge(s).
Very useful for laying out information, such as positioning a Label in the top center of my_container
:
local my_label = ui.Label{
+ bounds = ui.Bounds{size=ui.Size(1, 0.15, 0.1)}:move (
+ my_container.bounds.size:getEdge("top", "center")
+ )
+ }
+
Name | Type | Description |
---|---|---|
vertical | String | “top”, “center” (default) or “bottom”. |
horizontal | String | “left”, “center” (default) or “right”. |
depthwise | String | “front”, “center” (default) or “back”. |
Type | Description |
---|---|
vector | A vector (x, y, z) with coordinates corresponding to the requested position of the given Size object. |
A slider component for selecting a value from a range
Get or set the minimum selectable value
Name | Type | Description |
---|---|---|
newValue | ??? |
Nothing
Get or set the maximum selecrtable value
Name | Type | Description |
---|---|---|
newvalue | ??? |
Nothing
Get or set the current value
Name | Type | Description |
---|---|---|
newValue | ??? |
Nothing
A place to emit sound from. It can either play live-streamed audio or static sound files:
local leftSpeaker = ui.Speaker(ui.Bounds(0,0,0, 1,1,1))
+app:scheduleAction(0.02, true, function()
+ local leftAudio, rightAudio = player:generateAudio(960)
+ if left and leftSpeaker.trackId then
+ app.client.client:send_audio(leftSpeaker.trackId, leftAudio)
+ end
+end)
+
A view that stacks its subviews either vertical (default) or horizontally.
The StackView will adjust its size on the (specified) main axis while preserving the size given for the other axis. It will also adjust the size of each subview to fill the other axis while preserving the subview size on the main axis.
You can put StackViews into Stackviews to create rows and columns
rows = StackView(nil, "v")
+ rows:addSubview(Label{text="Example"})
+ cols = rows:addSubview(StackView(nil, "h"))
+ cols:addSubview(Label{text="Col1"})
+ cols:addSubview(Label{text="Col2"})
+ rows:addSubview(Label{text="The End"})
+ self:addSubview(rows)
+ rows:layout()
+
my_button = StackView(bounds, axis)
+
Name | Type | Description |
---|---|---|
bounds | Bounds | The StackView’s Bounds component |
axis | string | The main axis to layout subviews on. “v” (default) or “h” |
Nothing
Set the spacing between items
Name | Type | Description |
---|---|---|
newValue | number | The space between subviews. nil to just return the current value. |
Type | Description |
---|---|
number | The current value |
Layout the subviews
None
Nothing
TabView: Switch between different UIs using tabs
tabView = TabView(bounds)
+
Name | Type | Description |
---|---|---|
bounds | Bounds | The TabView’s bounds. |
tabSpacing | int | The spacing between tabs |
inactiveColor | Color | The color of an inactive tab |
activeColor | Color | The color of an active tab |
Nothing
A text field, used for inputting text.
Set onReturn
to define a function that gets called when the enter key is pressed (while the text field is focused):
my_textfield.onReturn = function()
+ -- do stuff
+end
+
Set onChange
to define a function that gets called when the contents of the text field have changed:
my_textfield.onChange = function()
+ -- do stuff
+end
+
Set onLostFocus
to define a function that gets called when the text field loses focus:
my_textfield.onLostFocus = function()
+ -- do stuff
+end
+
local textfield = TextField(o)
+
For convenience, you may also set some or all of the TextField’s properties within the constructor, i.e.:
local textfield = TextField{bounds=Bounds(0, 0, 0, 1.0, 0.1, 0.001), color={1.0,0.2,0.4,1}, halign="center"}
+
Name | Type | Description |
---|---|---|
o | table | A table including at least a Bounds component. |
Nothing
Appends the provided text to the TextField
Name | Type | Description |
---|---|---|
text | string | The text to append |
Nothing
A surface that allocates a video media track to use as texture Send frames like this
app:scheduleAction(0.02, true, function()
+ if surface and surface.trackId then
+ app.client.client:send_video(surface.trackId, pixeldata, width, height, [format=rgba8], [stride=width])
+ end
+end)
+
Initiate a video surface.
A video track will be allocated for the surface. The resolution can not be changed later.
Name | Type | Description |
---|---|---|
bounds | Bounds | The position and size of the surface |
resolution | Table | A table ({int, int}) with width and height giving the pixel resolution of the video. Must match the width and height sent to sendFrame |
Nothing
Set the input video resolution. Must be called before awakened.
Name | Type | Description |
---|---|---|
width | ??? | |
height | ??? |
Nothing
Set the output video encoder format. Default “mjpeg” which is slow but compatible. You can also set it to “h264” if you provide liballonet-av, libavcodec etc.
Name | Type | Description |
---|---|---|
fmt | ??? |
Nothing
Send a video frame to the server
Name | Type | Description |
---|---|---|
pixels | String | with pixel data according to format and stride |
width | int | The number of pixels in width. Should match the width set at init. |
height | int | The number of pixels in height. Shold match the height set at init. |
format | string | The pixel format. For example “bgrx8”. Default: “rgba” |
stride | int | The number of bytes for each row of pixels. Default: width*bpp |
Nothing
The View class acts as base for anything visual in an alloapp.
It manages a tree of sub-views; its bounds (transform and size) and a connection to a low-level entity.
view = View(bounds)
+
Name | Type | Description |
---|---|---|
bounds | Bounds | The View’s Bounds component |
Nothing
awake() is called when entity exists and is bound to this view.
None
Nothing
sleep() is called when the entity for the view stops existing
None
Nothing
does the entity for this view exist and is bound to this view?
None
Nothing
schedule something to do once the entity exists for this view. If it does, do the thing immediately.
Name | Type | Description |
---|---|---|
todo | function | The function to run |
Nothing
If this is set to true, user can grab and move this view using the grip button.
Name | Type | Description |
---|---|---|
grabbable | Boolean | Set to true to enable the View to be grabbed. |
grabOptions | table | A table of options for the grabbable component. See Components > grabbable |
Nothing
If this is set to true, the user’s cursor can land on this view, and you can receive pointer events. (See onPointerChanged
and friends)
Name | Type | Description |
---|---|---|
pointable | Boolean | Set to true to enable the View to receive pointer events. |
Nothing
The mat4 describing the transform from the parent view’s location to this view’s location, i e the location in the local coordinate system of the parent view.
None
Nothing
The mat4 describing the transform in world coordinates, i e exactly where the view is in the world instead of where it is relative to its parent.
None
Nothing
Converts point
from other
view to this view If other
is nil then the point is assumed to be in world space
Name | Type | Description |
---|---|---|
point | Point | A point in the coordinate system of other |
other | View | The view to convert the point from |
Nothing
The specification is used to describe the entity tree.
It is required to represent this view inside the Alloverse. In a subclass, call this implementation and then add/modify your own components.
None
Nothing
Asks the backend to update components on the server.
Use this to update things you’ve specified in :specification() but now want to change.
Name | Type | Description |
---|---|---|
changes | table | A table with the desired changes, for example: {transform={…}, collider={…}} |
removals | table | A table with the keys of componets to remove, for example: {“collider”, “skeleton”} |
Nothing
Mark one or more Components as needing to have their server-side value updated ASAP
Name | Type | Description | |
---|---|---|---|
components | string | {string} | either a string with one component to update, or a list if string components |
Nothing
Give this view an extra transform on top of the bounds. This is useful for things like adding a scale effect.
Name | Type | Description |
---|---|---|
transform | cpml.mat4 | The transformation matrix to set |
Nothing
If the entity backing this view has moved (e g grabbed by a user, or moved by an animation), this won’t automatically update the Pose in this view.
To make sure your local state reflects what is set in-world, you can call this method to update your Pose to match whatever transform is set on the entity in the world.
None
Nothing
Sets the View’s bounds (pose and size) in the world.
Note that simply changing a View’s Bounds won’t affect its size or position in the world until this method is run.
Name | Type | Description |
---|---|---|
bounds | Bounds | the Bounds with which to define the Size and Pose of the parent View in the world. |
Nothing
Uses superview’s bounds’ size to find an edge and move to that edge.
Returns the bounds so you can continue modifying it. See Size:getEdge()
Name | Type | Description |
---|---|---|
horizontal | ??? | |
vertical | ??? | |
depthwise | ??? |
Nothing
Adds a View as a child component to the given View.
Name | Type | Description |
---|---|---|
subview | View | The View to be added |
Nothing
Detaches the View from its parent
None
Nothing
Finds and returns a subview of the given view ID.
Name | Type | Description |
---|---|---|
vid | string | The viewId of the View to be searched for. |
Type | Description |
---|---|
View | The subview corresponding to the view ID. If no view was found, nil is returned. |
Plays the given sound asset ASAP.
You can use playOptions to set things like loop_count
, volume
, length
, offset
etc…
Name | Type | Description |
---|---|---|
asset | Asset | The asset to play |
playOptions | table | A table with valid keys for the “sound_effect” component |
Nothing
Ask the client that uses the given avatar to focus this view to take text input.
In other words: display the keyboard for that avatar.
Name | Type | Description |
---|---|---|
avatar | ??? |
Nothing
Dismiss the keyboard for the user that has currently keyboard-focused this view.
None
Nothing
Callback called when a user grabs this view in order to move it.
The server will then update self.entity.components.transform to match where the user wants to move it continuously. There is no callback for when the entity is moved.
Name | Type | Description |
---|---|---|
hand | Entity | The hand entity that started the grab |
Nothing
Callback called when a user lets go of and no longer wants to move it.
Name | Type | Description |
---|---|---|
hand | Entity | The hand entity that released the grab. |
Nothing
Callback for when a hand is interacting with a view.
NOTE: You must set view:setPointable(true), or the user’s cursor will just fall right through this view!
This is a catch-all callback; there is also onPointerEntered, onPointerMoved, onPointerExited, onTouchDown and onTouchUp if you want to react only to specific events.
Name | Type | Description |
---|---|---|
pointer | table | A table with keys (see below). |
The pointer
table’s keys are as follows:
hand
: The hand entity that is doing the pointingstate
: “hovering”, “outside” or “touching”touching
: bool, whether the hand is currently doing a poke on this viewpointedFrom
: a vec3 in world coordinate space with the coordinates of the finger tip of the hand pointing at this view.pointedTo
: the point on this view that is being pointed at (again, in world coordinates). |Nothing
Callback for when a hand’s pointer ray entered this view.
The state
in pointer
is now “hovering”
Name | Type | Description |
---|---|---|
pointer | table | see onPointerChanged. |
Nothing
Callback for when a hand’s pointer moved within this view.
The pointedFrom
and pointedTo
in pointer
now likely have new values.
Name | Type | Description |
---|---|---|
pointer | table | see onPointerChanged. |
Nothing
Callback for when the hand’s pointer is no longer pointing within this view.
The state
in pointer
is now “outside”
Name | Type | Description |
---|---|---|
pointer | table | see onPointerChanged. |
Nothing
Callback for when the hand’s pointer is poking/touching this view The state
in pointer
is now “touching”
Name | Type | Description |
---|---|---|
pointer | table | see onPointerChanged. |
Nothing
Callback for when the hand’s pointer stopped poking/touching this view.
This is a great time to invoke an action based on the touch. For example, if you’re implementing a button, this is where you’d trigger whatever you’re trying to trigger.
NOTE: If pointer.state is now “outside”, the user released the trigger button outside of this view, and you should NOT perform an action, but cancel it instead.
Name | Type | Description |
---|---|---|
pointer | table | see onPointerChanged. |
Nothing
an interaction message was sent to this specific view.
See Interactions
Name | Type | Description |
---|---|---|
inter | ??? | |
body | ??? | |
sender | ??? |
Nothing
Callback called when a file is dropped on the view
Name | Type | Description |
---|---|---|
filename | string | The name of the dropped file |
asset_id | string | The id of the asset dropped on you |
Nothing
Add an animation of a property of a component to this view For example, you might want to add a one-second animation of transform.matrix.rotation.x
from 0 to 6.28, repeating.
Name | Type | Description |
---|---|---|
anim | PropertyAnimation | The animation to add to this view. |
Nothing
Set the Views’s texture using an Asset.
The asset
parameter can be either an Asset instance or a raw string hash
View:setTexture(asset)
+
Name | Type | Description |
---|---|---|
asset | Asset | An instance of an Asset |
Nothing
Set the color of a View using a set of rgba values between 0 and 1.
E.g. to set the view to be red and 50% transparent, set this value to {1, 0, 0, 0.5}
View:setColor(rgba)
+
Name | Type | Description |
---|---|---|
rgba | table | A table defining a color value with alpha between 0-1. |
Nothing
Layout this view and its children. Override to implement your own custom layout. Don’t forget to call super’s implementation, and don’t forget to markAsDirty if needed.
None
Nothing
tiny_jpeg.h
Tiny JPEG Encoder
This is a readable and simple single-header JPEG encoder.
Features
This library is coded in the spirit of the stb libraries and mostly follows the stb guidelines.
It is written in C99. And depends on the C standard library. Works with C++11
==== Thanks ====
AssociationSirius (Bug reports) Bernard van Gastel (Thread-safe defaults, BSD compilation)
==== License ====
This software is in the public domain. Where that dedication is not recognized, you are granted a perpetual, irrevocable license to copy and modify this file as you see fit.
Issues a command.
Returns a json string that is valid until the next call to this method. If the issued command has a return value then you find it under the “return” key in the json. If any events has occurred since the last call to this method then the “events” key contains a chronoligical list of those events. sommand: a json object telling allonet what to do
Poll the allonet connection for data and events { “op”: “poll” } Connect to a place { “op”: “connect”, “url”: “alloplace://hello.place.alloverse.com”, “identity”: {“display_name”: “My App Name”}, “avatar_spec”: {} – TODO: Where is this format spec? } Interact with objects { “op”: “interaction”, “interaction”: {} – TODO: Where is this format spec? } Move about in the world { “op”: “intent”, “intent”: {} – TODO: Where is this format spec? }
This file proxies the network client to its own thread.
Terminology:
XR is currently the wild west when it comes to interaction patterns. We’re all learning what UX works in three dimensions, with different input devices and layouts.
In the Alloverse, a multitude of apps are to coexist in shared spaces. Thus, interacting with one app should be similar to how you interact with another app, instead of each app designing their own interaction patterns; otherwise it will be a too fragmented and confusing experience that decreases productivity and enjoyment rather than increasing it.
This document thus collects interaction guidelines, so that apps can behave in a straightforward, consistent and fun manner. It’s very much a work in progress. If you are an interaction designer, we would love your help in designing this document.
TODO: each section should have instructionals for dual-stick, single-stick and hands.
From the rule under “interactive”, VR movement in the Alloverse doesn’t use teleportation. Instead, movement happens with a static camera and an out-of-body experience. Hold down the A button, and use the dual sticks to move (left stick) and rotate (right stick). As you hold A, the camera stays in place while your avatar moves out in front of the camera, When you release A, the camera snaps into the head location of the avatar.
Since the sticks are not dedicated to movement by default, they can be used for character expressiveness and interaction.
Direct manipulation is the most intuitive way to interact with a computer.
You should:
You should avoid:
Point gesture
Pointing at an entity reveals its widgets, which allow you to:
Entities are informed when they are pointed at, and can react to it. The user can also push the trigger button to “poke” the entity at the pointed location. This is how you would push buttons,
In real life, any change between two states has some sort of transition. Nothing pops into existence in front of us unannounced; nothing disappears suddenly into nothingness. Your interface should be the same: every change should have a clear transition from its original state. This allows the user’s mind to understand what is happening, and build a mental model of the interaction.
Don’t teleport; move. Don’t swap meshes or textures; animate and transition.
Grabbable widget for scrolling
Responds to poke
So! You’ve built an Alloverse app, and now you want to host it on the Internet so that other people can use it. This is very similar to hosting a web app: you put some code on a server on the Internet, and then other people can access that server, and run the code on that server to access your web app in their browser.
The one thing that is a bit weird to get your head around is that an alloapp isn’t accessed directly by the end user’s browser! Instead, the APP talks to the PLACE, and then the PLACE updates its world representation and sends this new world to the USER’s visor.
When you’re running an alloapp on your local development machine, you have used ./allo/assist run alloplace://sandbox.places.alloverse.com
or something similar to connect your app directly to a specific place. This is good for debugging, or for demoing something to someone once. However, it won’t let other users in other Places launch your app, and also your app will stop running in the Place e g when your laptop goes to sleep.
In production, you can run ./allo/assist serve
on a dedicated server to start a http gateway. When an alloplace accesses this gateway, it will start a new instance of your alloapp, and connect it to the alloplace that asked for it. This is how you put your app properly on the internet, and let other people launch it to their Alloverse Places.
alloapp:
URL on their computer or headsetlaunch_app
on the connected Place, asking it to launch the app on behalf of the user.In the future, there will be a Dockerfile in the standard alloapp template. For now, you can do something like this:
./allo/assist serve
in the root of your project on bootproxy_pass
to http://localhost:8000
(which is where the gateway is running). (this is technically optional, but recommended)You can now figure out the URL of your app. If your server is example.com
, your gateway is now available over HTTPS at https://example.com
; or if you didn’t configure nginx or ssl, at http://example.com:8000
. Take this URL, and prepend alloapp:
, so you get something like: alloapp:https://example.com/
. That’s your app’s URL!
In the hopefully not too distant future, you’ll be able to log onto my.alloverse.com to register your app metadata and URL. This will then publish your app to the Marketplace inside Alloverse, so any user can access it.
Alloverse is an open platform for collaborative workspaces in 3D. It’s Gibson-style Cyberspace - but for day-to-day work and play with friends and colleagues. It’s a VR and AR platform for creating spaces, and for running real applications within those spaces, together with other people. In nerd terms, it’s a VR/AR/3D “window manager” and collaborative workspace.
For more information, please see the Alloverse website.
An Alloverse app is a server-side app that you run on your own server, similar to how you would host a web app on your own server. The difference is, when a user goes to your app on the web, it loads into that user’s web browser on their computer; but when a user goes to your alloapp in an Alloverse Place, the app loads into the Place for all users in that Place to use, and your code gets collaboration and VR super-powers for free.
This tutorial will take you through creating an Alloverse app using the Lua programming language, since that is the language we’ve developed the most support for so far.
You can build apps on Mac, Linux or Windows. On Windows, you’ll need a Unix shell, preferrably WSL2 (but mingw/msys2 could also work if you’re lucky): follow this WSL installation guide and install Ubuntu.
In contrast to a web framework like Rails or Django, Alloverse doesn’t install any software onto your system. Instead, a project is completely self-contained within its project folder, and contains everything it needs to run.
Note that building alloapps is done completely in text (through VSCode and Terminal), and there are currently no graphical editors (like the scene editor you might be used to from Unity or Unreal).
To write your own Alloverse app, you’ll need the following:
git
into a terminal, it’ll ask to install it for you even without Xcode).Let’s begin by opening the window to Alloverse by getting the Visor running and connected to a Place. Open the app you just downloaded, press the “Connect” button and enter “Sandbox” in the input field. Alternatively, if you’re on a Mac, clicking this link will automatically do the above for you.
That’s it - you’re now inside the Alloverse Sandbox Place, providing you a way to view and interact with the app you’ll be developing in the next step.
Now, let’s use the terminal to create a project folder, and ask AlloAssist to initialize a base project with all dependencies into it. From your terminal:
If all went well so far, you should now be able to run ./allo/assist help
to review all available commands. This might also be a good time to run git commit -a
, so the next commit only contains your own changes.
Anyway, the curl’d script will have created this project structure for you:
lua
contains the Lua source code for your app lua/main.lua
contains your app! It barely does anything yet. But you can add more code to this file to keep going, and add more files into lua to modularize your code.images
contains images you want to use as textures on buttons, etc.allo
contains the Alloverse app framework. You should not edit anything in here, as an upgrade of the framework will overwrite your changes. allo/assist
is a script that helps you with tasks such as starting a server, installing dependencies, etc…allo/libs
contains compiled C dependencies as dynamic libraries and executables. These are .gitignore
d to save space. If you check out your code on another computer, you can ./allo/assist fetch
to install them again, so you don’t need to (and shouldn’t) commit them.allo/boot.lua
sets up the runtime environment for your app. This code is generated, so it’s not recommended to modify this file.You should now be able to make your app appear in an Alloplace! In the terminal, run the following command:
If everything checks out, go to the Alloverse Visor, where you should be able to see and interact with the app and its button. (If you’ve moved around in the place, you may need to look around to see it)
Our app running in Nevyn’s place. I’ve clicked our simple app’s orange button button three times, and the laws of causality held up, printing “Hello” thrice to our terminal.
At this point, you could go off, do your own thing, read the documentation as you go. Or you could keep reading, and be guided through the development of a simple but functional todo list app, which will help establish the fundamentals and make it much easier for you to build real, complex apps.
If your app ever disappears, take a look around in the Alloverse to make sure you’re not just looking in the wrong direction. If you still can’t see it, it might have crashed. Check out the terminal from which you ran it - there should be error messages to help you figure out what went wrong.
This is also a good point at which to remind you: if you get stuck, or have questions or feedback, please hop on Discord and give it to us straight.
Let’s open up lua/main.lua
and have a look at the code, to understand what’s going on. If you’re not much for theory, you may skip this section and get straight to making an app.
Above, we’re using a Client to connect this app to a Place. arg[2]
is the URL of the place to connect to, which Assist sets up for you in boot.lua. App then manages the Client connection for you, and manages the lifetime of your app.
Assets are files (images, glb models, videos, sounds, etc…) that you want to use in your app. They need to be published so that users’ visors can download them before you can use them. We make assets
global so you can use it throughout your app.
Your application is represented by a hierarchy of views. If you’re a web developer, you can think of it as nested elements in a DOM. If you come from Unity, it’s similar to the GameObject hierarchy, with transforms being relative to parent GameObjects. If you’re an oldschool Macintosh Toolbox developer, I’m sorry.
app.mainView
is the main UI for your app. You should set it up with your main user interface before connecting to the Place.
Bounds
is the position and size of your object:
0, 1.2, -2
means the mainView
is centered horizontally 1.2 meters up from the floor and 2 meters into the Place, depth-wise.1, 0.5, 0.01
defines the object as being 1 meter wide, half a meter tall, and one centimeter deep.We also make the main view grabbable
, so the user can move it around the Place. Alternatively, you can add a GrabHandle, which acts like the title bar of a desktop UI’s window.
The standard template has created a standard Button component for you, at 2 decimeters wide and tall. Its position of 5 centimeter down and forward is relative to its parent - in this case the mainView
.
The onActivated
callback lets us run code when the button is pressed. This’ll print “hello” to your local terminal when you run the app on your machine (even if you’re running the visor somewhere else on the Internet)!
Finally, the template has configured the app’s mainView
to use this dummy UI. Then, it connects to the Place we’ve been asked to run in, and finally hands over the runtime of the app to the UI framework so it keeps running until the user quits it.
Now that we got the gist of how to compose an app like this, we can replace it with something useful.
Let’s make a todo list app! I’m a master artist, so I’ve painted this piece of art to represent our goal:
So we’ve got:
Sounds great. Let’s get started.
Note: This is where we start coding for real.
Select everything from (and including) local mainView = ...
down to app.mainView = mainView
and delete it, and we’ll write some new, fun code.
Instead of having a bunch of loose views strewn around the code, I enjoy creating a class for each main container of UI. Some equivalent of a “window”, or “view controller”, or “component” if you will. Let’s create a TodosView
, which will display the list of items, and allow you to quit the app, and add new items. Copy the code block below, and paste it between app.assetManager:add(assets)
and app:connect()
.
We’re using Penlight’s class
macro to create classes. We inherit ui.Surface, which is like a ui.View except it also draws a background, so our app gets a backdrop to rest our controls onto.
We create a constructor, where we set up initial state and controls. Here we want a quit Button, and an add Button. We immediately add them as subviews to the TodosView, so they show up in the view hierarchy. For the first button, we use one of our image assets as a texture, and for the other, we set the text on the label instead.
AlloUI doesn’t have a comprehensive layout system itself, so we’ll call our own layout
at opportune moments. Let’s stub it out so we have something to start with. We’ll also create an instance of it, and use it as our app’s main view. Paste the following into your code, just below the _init
function:
Let’s restart your app to see your new code changes. Open the terminal where your app is running, and quit it with ctrl-C. Then, fire off a quick ./allo/assist run alloplace://sandbox.places.alloverse.com
in your terminal, and you’ll be presented with this beauty in Alloverse:
We’ve created a VR interface, how cool is that!? Just keep in mind that while the “Add todo” button is is visible, clicking it will cause the app to crash. So, let’s attach some functionality to it: let’s make it create that popup for inputting new todo items. Paste the following code block right underneath the layout()
function:
Phew, that’s a handful. Let’s step through it.
popup
is our new popup window. It’s another surface, one meter by a half.done
.onReturn
is used to react to return/enter key, and we can also use it to make sure a newline character isn’t added to the text.showNewTodoPopup
has been called with a hand
. That’s the hand of the user that tapped the “Add todo button”. It belongs to an avatar, so we can actually ask the avatar if it would pretty please focus this text field, so the user can begin typing in it directly? If you’d be so kind.addButton
uses the same done
callback as onReturn
cancelButton
just closes the popup, no questions asked.app:openPopupNearHand
will display our new fancy popup 60 centimeters away from the user’s hand, which is a convenient distance at which to do your job.Warning: incoming rant
The cool thing about this popup is that if you have multiple people in the room, they can all get their own input popup, so they can add their own items without interfering with each other. This is an important and unusual aspect of UI design and development: all your UI is real time collaborative, so you have to consider how your application behaves when multiple people use it at the same time.
For example, if you had added the input field directly to the main view, only one user would’ve been able to add items at a time, and they’d likely have interfered with each other’s work. By using a personal popup, users can work naturally collaboratively and in tandem.
Relax: rant over.
Here’s what this new fancy popup should look like once you tap the “Add todo” button:
Before we continue, we’ll need another asset. Download checkmark.png and put it in images/
. Then we’ll have to refer to it from code. At the top of your file, you’re already loading the quit
texture asset. Modify that block to also load and publish the checkmark
texture, by making the code block look like this:
Cool. Cool cool cool. Let’s make it actually possible to add todo items, yeah? Add these two functions below your existing ones:
This should start to look familiar.
todoView
is the parent view of all the controls related to the todo list item. We store them in self.todoViews
so we can layout them properly later.checkButton
is used to check off a completed item. We place it along the left edge, tell it to use our new fancy checkmark texture, and configure it to call an as-of-yet nonexistent removeTodo
method.label
is the text label describing our item. Text is black (that’s a list of Red, Green, Blue and Alpha, with components from 0.0 to 1.0), horizontally aligned to the left, with the text from the user.layout
so our new item is positioned correctlyremoveTodo
just finds the given item in the list, and removes it from both the list and the UI, running a relayouting pass to adjust the other items’ positions.
That’s awesome! We can now add and remove todo list items! Just one bummer: they all end up stacked on top of each other. We’ll need to lay them out before this is usable. Let’s replace our dummy layout
with something better. Remove the existing contents of your layout
function, and type in this:
Here’s a pattern I’ve brought with me all the way back from building black-and-white Mac apps in the 80s: using a pen
variable which represents a rectangle (or rather, a 3D box!) where we want to “stamp out” our UI, moving the pen as we walk through our items.
You know what? I think this is pretty stellar. This’ll do nicely. If you dotted every i, your app should look something like this:
So, you built your fancy VR app. Now you want to show it off, and deploy it so all the Alloverse users in the world can run it in their places, right?
Well, you’re in luck! Go ahead and learn about hosting an Alloverse app!
If you made it this far, go eat a cinnamon roll, you deserve it. It’s on me. Really, just ping me on Discord and it’s yours. We’d at least love to hear from you, and see your creations.
Once you make your own apps, even if you’re not down with cinnamon rolls, do let us know, so we can feature your app in our app browser, blog and tweet about it, etc etc.
If you’re feeling ambitious, you can boot a Place of your own using the docker run -e ALLOPLACE_NAME="my place" -p 21337:21337/udp -it alloverse/alloplace2
command. In the Alloverse connect input, enter the name of the place you just created.
You’ll find this post in your _posts
directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run jekyll serve
, which launches a web server and auto-regenerates your site when a file is updated.
Jekyll requires blog post files to be named according to the following format:
YEAR-MONTH-DAY-title.MARKUP
Where YEAR
is a four-digit number, MONTH
and DAY
are both two-digit numbers, and MARKUP
is the file extension representing the format used in the file. After that, include the necessary front matter. Take a look at the source for this post to get an idea about how it works.
Jekyll also offers powerful support for code snippets:
Check out the Jekyll docs for more info on how to get the most out of Jekyll. File all bugs/feature requests at Jekyll’s GitHub repo. If you have questions, you can ask them on Jekyll Talk.
Copyright (c) 2018, Nevyn Bengtsson All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Alloverse is open source. You can read about Alloverse’s license in its LICENSE file.
In addition, Alloverse in turn uses a number of open source projects to work. The licenses of these projects are listed below.
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
CPML is Copyright (c) 2016 Colby Klein shakesoda@gmail.com.
CPML is Copyright (c) 2016 Landon Manning lmanning17@gmail.com.
Code in vec3.lua is derived from hump.vector. (c) 2010-2013 Matthias Richter. MIT.
Portions of mat4.lua are from LuaMatrix, (c) 2010 Michael Lutz. MIT.
Code in simplex.lua is (c) 2011 Stefan Gustavson. MIT.
Code in bound2.lua and bound3.lua are (c) 2018 Andi McClure. MIT.
Code in quat.lua is from Andrew Stacey and covered under the CC0 license.
Code in octree.lua is derived from UnityOctree. (c) 2014 Nition. BSD-2-Clause.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Copyright (c) 2002-2016 Lee Salzman
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Copyright (c) 2018 Aramis Group, LLC dba code lever
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Apache License Version 2.0, January 2004 http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Definitions.
“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
“Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
“Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
“You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.
“Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
“Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
“Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
“Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
“Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.”
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
(d) If the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to http://unlicense.org
Copyright 2017 Michał Muskała
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Copyright (c) 2020 Bjorn Swenson and other LÖVR contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Copyright © 1994–2019 Lua.org, PUC-Rio.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Oculus Software Development Kit License Agreement
Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. The text of this may be found at: https://developer.oculus.com/licenses/sdk-3.5/
In order to obtain and use the Oculus Software Development Kit for mobile or for PC, You must first agree to the terms of this License. If you agree to the terms of this License, you may use the Oculus Software Development Kit. If you do not agree to the terms of this License, then you may not use the Oculus Software Development Kit.
OCULUS SDK LICENSE
1.1 This license grants you the non-exclusive license and right to use (i) the Oculus SDK to make engines, tools, applications, content, games and demos (collectively and generally referred to as “Developer Content”) for use on the Oculus approved hardware and software products (“Oculus Approved Products”) and which may incorporate the Oculus SDK in whole or in part in binary or object code; and (ii) the headers, libraries, APIs and other tools made available by Oculus to enable the use of Platform Services with your Developer Content.
1.2 For the sake of clarification, when you use the Oculus SDK in or with Developer Content, you retain all rights to your Developer Content, and you have no obligations to share or license Developer Content (including your source and object code) to Oculus or any third parties; provided, however, Oculus retains all rights to the Oculus SDK and the headers, libraries and APIs to the Platform Services and other tools made available by Oculus, including those that may be incorporated into your Developer Content.
1.3 You agree that as a condition of this License you will design and distribute your Developer Content to ensure that your Developer Content and any software required to use your Developer Content does not, and you will not, alter or interfere with the normal operation, behavior or functionality of the Oculus hardware or software or Oculus Approved Products, including: (i) the behavior of the “Oculus button” and “XBox button” implemented by the Oculus system software; (ii) any on-screen messages or information; (iii) the behavior of the proximity sensor in the Oculus hardware implemented by the Oculus system software; (iv) Oculus hardware or software security features; (v) end user’s settings; or (vi) the Oculus Flash Screen Warnings. You also agree not to commit any act intended to interfere with the normal operation of the Oculus hardware or software or Oculus Approved Products, or provide software to Oculus users or developers that would induce breach of any Oculus agreements or that contains malware, viruses, hacks, bots, Trojan horses, or other malicious code.
1.4 You may not use the Oculus SDK for any purpose not expressly permitted by this License. You may not (except as and only to the extent any following restriction is prohibited by applicable law): (a) decompile; (b) reverse engineer; (c) disassemble; (d) attempt to derive the source code of the Oculus SDK or any part of the Oculus SDK, or any other software or firmware provided to you by Oculus
REDISTRIBUTION
2.1 You may sublicense and redistribute the source, binary, or object code of the Oculus SDK in whole for no charge or as part of a for-charge piece of Developer Content; provided, however, you may only license, sublicense or redistribute the source, binary or object code of the Oculus SDK in its entirety. The Oculus SDK (including, but not limited to LibOVR and VRLib), and any Developer Content that includes any portion of the Oculus SDK, may only be used with Oculus Approved Products and may not be used, licensed, or sublicensed to interface with software or hardware or other commercial headsets, mobile tablets or phones that are not authorized and approved by Oculus;
2.2 You must include with all such redistributed or sublicensed Oculus SDK code the following copyright notice: “Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.” 2.3 You must give any other recipients of the Oculus SDK a copy of this License as such recipients, licensees or sublicensees may only use the Oculus SDK subject to the terms of this License and such recipient’s, licensee’s or sublicensee’s agreement to and acceptance of this License with Oculus; and
2.4 The Oculus SDK includes a “LICENSE” text file (the “License Notice”), and any Oculus SDK distribution that you distribute must include a copy of this License with the License Notice.
OCULUS PLATFORM SERVICES
“Platform Services” means the Oculus Platform Framework and the Application Services.
3.1 Oculus Platform Services. Oculus makes certain Platform Services available to you to include and enable in your Developer Content. Developer Content that enables or includes any Platform Service must implement the Oculus Platform Framework with that Developer Content. Once your Developer Content has been authorized for use of the Platform Services, you are not required to update your Developer Content to include new Platform Services Oculus may make available as part of the Oculus Platform Framework.
3.2 Limited Authorization. You hereby grant Oculus the limited authorization reasonably necessary for Oculus’s exercise of its rights and performance of its obligations under this Section 3. You agree that Oculus may use its contractors and affiliates for the purposes of exercising its rights and licenses set forth in this Section 3.
3.3. Internal Use. You agree that Oculus may grant its employees and internal contractors the right to use, perform and display the Developer Content you provide to Oculus for testing, evaluation and approval purposes, which shall be on a royalty-free basis.
3.4 Key Provision and Redemption. If you request that Oculus generate activation keys for your Developer Content on the Platform (“Keys”) and Oculus agrees, you hereby grant Oculus (i) the right to generate Keys for you and (ii) a license to make available, reproduce, distribute, perform, and display the Developer Content to end users who have submitted a Key to Oculus. Oculus agrees to authenticate and make Developer Content available to any end user supplying a valid Key (unless the Developer Content has been removed or withdrawn).
3.5 Platform Services Requirements. You will not make any use of any API, software, code or other item or information supplied by Oculus in connection with the Platform Services other than to enhance the functionality of your Developer Content. In particular, you must not (nor enable others to): (i) defame, abuse, harass, stalk, or threaten others, or to promote or facilitate any prohibited or illegal activities; (ii) enable any functionality in your Developer Content that would generate excessive traffic over the Oculus network or servers that would negatively impact other users’ experience, or otherwise interfere with or restrict the operation of the Platform Services, or Oculus’s servers or networks providing the Platform Services; or (iii) remove, obscure, or alter any Oculus license terms, policies or terms of service or any links to or notices thereto. You may not sublicense any software, firmware or other item or information supplied by Oculus in connection with the Platform Service for use by a third party, unless expressly authorized by Oculus to do so. You agree not to use (or encourage the use of) the Platform Services for mission critical, life saving or ultra-hazardous activities. Oculus may suspend operation of or remove any Developer Content that does not comply with the restrictions in this License. You will not use the Oculus Avatar associated with the Oculus ID of any end user in your Developer Content without the express permission of that end user unless, (i) that end user is actively engaged with your Developer Content or (ii) that end user remains part of an active session of your Developer Content with whom other end users are interacting, whether or not that end user is then online.
GENERAL PROVISIONS
4.1 Oculus may include in this Oculus SDK additional content (e.g., samples) for demonstration, references or other specific purposes. Such content will be clearly marked in the Oculus SDK and is subject to any included terms and conditions.
4.2 Your use of third-party materials included in the Oculus SDK may be subject to other terms and conditions typically found in separate third-party license agreements or “READ ME” files included with such third-party materials. To the extent such other terms and conditions conflict with the terms and conditions of this License, the former will control with respect to the applicable third-party materials.
THE OCULUS SDK AND ANY COMPONENT THEREOF, THE OCULUS HEADERS, LIBRARIES AND APIS, AND THE PLATFORM SERVICES FROM OCULUS AND ITS CONTRIBUTORS ARE PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OCULUS AS THE COPYRIGHT OWNER OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS OCULUS SDK, THE OCULUS HEADERS, LIBRARIES AND APIS OR THE PLATFORM SERVICES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOME JURISDICTIONS DO NOT PERMIT THE EXCLUSION OR LIMITATION OF IMPLIED WARRANTIES, SO YOU MAY HAVE ADDITIONAL RIGHTS.
This License does not grant permission to use the trade names, trademarks, service marks, or product names of Oculus, except as required for reasonable and customary use in describing the origin of the Oculus SDK, and reproducing the content of the License Notice file. Oculus reserves all rights not expressly granted to you under this License. Neither the name of Facebook Technologies, LLC, Oculus VR, LLC, Oculus, nor the names of Oculus’s contributors, licensors, employees, or contractors, may be used to endorse or promote products developed using the Oculus SDK without specific prior written permission of Oculus.
You are responsible for ensuring that your use of the Oculus SDK and your Developer Content, including enabled Platform Services, complies with all applicable laws (including privacy laws) wherever your Developer Content is made available. You acknowledge and agree that you are solely responsible for any health and safety issues arising from your Developer Content. You will not collect end users’ content or information, or otherwise access any Oculus site, using automated means (such as harvesting bots, robots, spiders, or scrapers) without Oculus’ prior permission.
Your acceptance of the terms and conditions of this License in and of itself and for all Developer Content created as of March 28, 2016, may be evidenced by any of the following: your usage of the Oculus SDK, or acceptance of the license agreement. As this License is updated for future releases of the Oculus SDK, you agree to abide by and meet all requirements of future updates of this License for those future Oculus SDK releases, with acceptance evidenced by usage of the Oculus SDK or any element thereof and the future updates of this License will apply for that future Developer Content that may be developed for or with that future Oculus SDK or any element thereof (i.e., you cannot sidestep out of the requirements of future updates of the License by developing against an older release of the Oculus SDK or License).
Oculus reserves the right to terminate this License and all your rights hereunder immediately in the event you materially breach this License.
Furthermore, Oculus also reserves the right to cancel or terminate this License for any of the following reasons:
a. Intellectual property infringement by you with Developer Content created by you that is used with or by the Oculus SDK, or any of the Platform Services; b. Developer Content (including enabling Platform Services) that violates applicable law; c. Health and safety issues associated with your Developer Content; d. Failure to comply with or use properly the Oculus Flash Screen Warnings; e. Use of the Oculus SDK with a commercial product other than an Oculus Approved Product; f. Failure to provide required notices as set forth above; and g. Failure to observe the restrictions in Section 3.5.
You agree to fully indemnify Oculus from any and all losses, costs, damages and expenses (including reasonable attorney’s fees) arising out of your Developer Content or any matter set forth in Sections 6, 7 and 10(a) through (g).
Oculus may discontinue or change functionality of the Platform Services at any time, and your continued use of the Platform Services or use of any modified or additional Platform Services is conditioned upon your adherence to the terms of this License, as modified by Oculus from time to time.
In the event any provision of this License is determined to be invalid, prohibited or unenforceable by a court or other body of competent jurisdiction, this License shall be construed as if such invalid, prohibited or unenforceable provision has been more narrowly drawn so as not to be invalid, prohibited or unenforceable.
You may not assign any rights or obligations under this License without the advance written consent of Oculus, which may be withheld in its sole discretion. Oculus may assign its rights or obligations under this License in its sole discretion.
Failure of either party at any time to enforce any of the provisions of this License will not be construed as a waiver of such provisions or in any way affect the validity of this License or parts thereof.
Your remedies under this License shall be limited to the right to collect money damages, if any, and you hereby waive your right to injunctive or other equitable relief.
You will comply, and will not cause Oculus to not comply (by for example, providing Developer Content to Oculus under this Agreement for which required export clearances have not been obtained), with all applicable export control laws of the United States and any other applicable governmental authority, including without limitation, the U.S. Export Administration Regulations. You agree that this License and the Oculus SDK and accompanying documentation are Oculus’s confidential information (and is not publicly available), and you will not use it, disclose it or make it available to others except in accordance with the terms of this License.
This License shall be governed by the laws of the State of California, without giving effect to conflict of laws provisions or principles thereof. The parties agree that, except as provided below, all disputes relating to this License shall be resolved by binding non-appearance-based arbitration before a single neutral arbitrator in San Francisco, California. The arbitration will be conducted in the English language by a single arbitrator who is an attorney-at- law with at least fifteen (15) years’ experience in consumer and technology transactions and who is also a member of the JAMS roster of arbitrators. If You and Oculus cannot agree on a mutually acceptable arbitrator within thirty (30) days after the arbitration is initiated, then JAMS will pick a neutral arbitrator who meets such qualifications. The arbitration shall be conducted in accordance with the rules and procedures of JAMS then in effect, and the judgment of the arbitrator shall be final and capable of entry in any court of competent jurisdiction. The parties undertake to keep confidential all awards in their arbitration, together with all materials in the proceedings created for the purpose of the arbitration and all other documents produced by another party in the proceedings not otherwise in the public domain, save and to the extent that disclosure may be required of a party by legal duty, to protect or pursue a legal right or to enforce or challenge an award in legal proceedings before a court or other judicial authority. You and Oculus agree the following may be submitted to a court of competent jurisdiction located within San Francisco, California and further agree to submit to the personal jurisdiction of the courts located within San Francisco, California in connection with (a) any entrance of an arbitrator’s judgment or decision, (b) any dispute with respect to the arbitration process or procedure, (c) Oculus’ exercise of any of its equitable rights or remedies or (d) any claims regarding the ownership, validity, enforceability and/or infringement of any intellectual property rights.
Copyright 2001-2011 Xiph.Org, Skype Limited, Octasic, Jean-Marc Valin, Timothy B. Terriberry, CSIRO, Gregory Maxwell, Mark Borgerding, Erik de Castro Lopo
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS’’ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Opus is subject to the royalty-free patent licenses which are specified at:
Xiph.Org Foundation: https://datatracker.ietf.org/ipr/1524/
Microsoft Corporation: https://datatracker.ietf.org/ipr/1914/
Broadcom Corporation: https://datatracker.ietf.org/ipr/1526/
Copyright (C) 2009-2016 Steve Donovan, David Manura.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Copyright (c) 2014 Chris McCord
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+HEREUNDER.
+
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an “owner”) of an original work of authorship and/or a database (each, a “Work”).
Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works (“Commons”) that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others.
For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the “Affirmer”), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.
i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person’s image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.
Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer’s Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the “Waiver”). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer’s heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer’s express Statement of Purpose.
Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer’s express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer’s Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the “License”). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer’s express Statement of Purpose.
Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person’s Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.
The MIT License (MIT)
Copyright (c)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.]
Preamble
+
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software–to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some specially designated software packages–typically libraries–of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things.
To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author’s reputation will not be affected by problems that might be introduced by others.
Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs.
When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library.
We call this license the “Lesser” General Public License because it does Less to protect the user’s freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances.
For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system.
Although the Lesser General Public License is Less protective of the users’ freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a “work based on the library” and a “work that uses the library”. The former contains code derived from the library, whereas the latter must be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
A “library” means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables.
The “Library”, below, refers to any such software library or work which has been distributed under these terms. A “work based on the Library” means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term “modification”.)
“Source code” for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library.
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does.
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
a) The modified work must itself be a software library.
+
+b) You must cause the files modified to carry prominent notices
+stating that you changed the files and the date of any change.
+
+c) You must cause the whole of the work to be licensed at no
+charge to all third parties under the terms of this License.
+
+d) If a facility in the modified Library refers to a function or a
+table of data to be supplied by an application program that uses
+the facility, other than as an argument passed when the facility
+is invoked, then you must make a good faith effort to ensure that,
+in the event an application does not supply such function or
+table, the facility still operates, and performs whatever part of
+its purpose remains meaningful.
+
+(For example, a function in a library to compute square roots has
+a purpose that is entirely well-defined independent of the
+application. Therefore, Subsection 2d requires that any
+application-supplied function or table used by this function must
+be optional: if the application does not supply it, the square
+root function must still compute square roots.)
+
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library.
In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of the Library into a program that is not a library.
If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code.
However, linking a “work that uses the Library” with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a “work that uses the library”. The executable is therefore covered by this License. Section 6 states terms for distribution of such executables.
When a “work that uses the Library” uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself.
You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things:
a) Accompany the work with the complete corresponding
+machine-readable source code for the Library including whatever
+changes were used in the work (which must be distributed under
+Sections 1 and 2 above); and, if the work is an executable linked
+with the Library, with the complete machine-readable "work that
+uses the Library", as object code and/or source code, so that the
+user can modify the Library and then relink to produce a modified
+executable containing the modified Library. (It is understood
+that the user who changes the contents of definitions files in the
+Library will not necessarily be able to recompile the application
+to use the modified definitions.)
+
+b) Use a suitable shared library mechanism for linking with the
+Library. A suitable mechanism is one that (1) uses at run time a
+copy of the library already present on the user's computer system,
+rather than copying library functions into the executable, and (2)
+will operate properly with a modified version of the library, if
+the user installs one, as long as the modified version is
+interface-compatible with the version that the work was made with.
+
+c) Accompany the work with a written offer, valid for at
+least three years, to give the same user the materials
+specified in Subsection 6a, above, for a charge no more
+than the cost of performing this distribution.
+
+d) If distribution of the work is made by offering access to copy
+from a designated place, offer equivalent access to copy the above
+specified materials from the same place.
+
+e) Verify that the user has already received a copy of these
+materials or that you have already sent this user a copy.
+
For an executable, the required form of the “work that uses the Library” must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute.
a) Accompany the combined library with a copy of the same work
+based on the Library, uncombined with any other library
+facilities. This must be distributed under the terms of the
+Sections above.
+
+b) Give prominent notice with the combined library of the fact
+that part of it is a work based on the Library, and explaining
+where to find the accompanying uncombined form of the same work.
+
You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it.
Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients’ exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License.
If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and “any later version”, you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation.
If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
NO WARRANTY
+
BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License).
To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your school, if any, to sign a “copyright disclaimer” for the library, if necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob’ (a library for tweaking knobs) written by James Random Hacker.
Incomplete
Please begin by reading the architecture documentation, as it explains how all these components fit together.
To see how these messages actually sent over the network, see Wire Protocol. This document also specifies the differences between the specification and the current implementation.
An agent is code that can spawn entities, and communicates to a place on behalf of the entities it owns. Thus, all these are agents:
Every connected agent has to spawn its “avatar” entity as part of its ANNOUNCE
message, so that it has a representation in a place and can be communicated with (or terminated, in the case of apps).
When an agent disconnects from a place, all its entities are removed from the place.
An agent’s ANNOUNCE
message contains its identity: display name, profile URL, and a certificate/public key that can be used to identify the same user/app across connections and places without a central identity authority server.
Identity is currently expressed as the following JSON blob:
{
+ "display_name": "annie"
+}
+
An entity is the manifestation of an agent in a place. The only fixed information in an entity is its id
. Everything else is specified as a set of components.
JSON specification:
{
+ "id": "1234",
+ "components": {
+ "transform": // every component has a string key
+ { // and an object value
+ // the keys and values should be consistent with
+ // with the specification for that key. See "Component"
+ }
+ }
+}
+
A component describes an aspect of an entity, such as:
transform
: its position, rotation and sizemesh
: description of its physical geometryAlloverse defines a set of official components. App developers are free to invent their own components, which is useful for app-to-app communication (though a standard Visor will not be able to interpret them).
The “intent” blob is used every heart-beat to indicate how the agent intends that its avatar (and other owned entities) should move and behave in real-time this frame. It only covers basic movement: more complex behavior is covered by “interactions”. It’s sent over the unreliable channel, so it should be repeated every frame as long as it’s relevant.
The blob is also used to send housekeeping information such as acknowledging receipt of state diffs.
See Intent.
An on-demand message either announcing information, or requesting something from one entity to another (sent by the requesting entity’s agent, and handled by the receiving entity’s agent).
Alloverse defines a set of official Interactions. App developers are free to invent their own interactions, which is useful for app-to-app communication (though a standard Visor will not be able to interpret them).
Agent to place per-frame message.
See Intent.
Sent from agent, to place, then forwarded to the designated agent, over the reliable channel on demand.
[
+ "interaction",
+ "{oneway|request|response|publication}"
+ "{source entity ID}",
+ "{destination entity ID or emptystring if publication}",
+ "{request ID or empty string if single-way}",
+ // interaction body goes here
+ ]
+
The agent sending the request must own the source entity.
If the second field is set to “request” and a request ID is set, the recipient can respond to the interaction by setting “response” and filling in the same request ID, and the alloplace server will route the message correctly.
A response must be sent to the entity that sent the request. Sending a response to a an entity that didn’t request it may lead to force disconnection.
The client sets its own request IDs. These must be unique for each request this session for this agent. An UUID or monotonically increasing integer as string should do.
If the second field is “publication” and no destination entity is filled in, it’s a publication that anyone can subscribe to. This is useful for apps to broadcast information that is instantaneous rather than a property of an entity (in which case it would have been a property of a component of an entity).
Agents can subscribe to publications by providing a “match pattern” which is an Elixir guard expression. This lets agents set very fine grained subscriptions to exactly what interactions it wants to receive.
If nobody is subscribing to a matching pattern of the given publication, the message is filtered out by the alloplace server.
Behavior is undefined if you provide a request id in a publication. Future versions may allow publications to have responses.
The message is directed to a single entity/client, and you can’t respond to it. request_id
should be empty.
Access control is defined on the interaction level. The alloplace server can be fed with Elixir guard expressions the same way that subscriptions work, and an “allow” or “deny” flag, which then becomes the “Access Control List” (ACL) for interactions in the place.
Since announce is an interaction, an interaction ACL can be used to allow/deny agents from joining a place. Likewise, since changing the ACL is also an interaction, you can use an ACL to control who may modify the ACL. In other words, anything except intents and place state updates can be filtered using interaction ACLs.
Interaction ACLs guard on the entire interaction message, and not just the payload body. So for example, the following rule would disallow users named “harry” from joining a room:
[
+ "deny",
+ """[
+ "interaction", // match exactly on provided value for first field
+ "request",
+ SourceEntity, // bare words become variables
+ "place",
+ _, // underscore means unused variable = ignored value
+ [
+ "announce",
+ ["identity", Identity],
+ _
+ ]
+ ] where Identity["name"] == "harry" """
+]
+
Note that the rule itself is an Elixir string, not a JSON expression.
If an interaction is sent by an agent but is disallowed by an ACL rule, the following message is sent in response:
[
+ "interaction_denied",
+ // original interaction goes here,
+ // rule that caused denial goes here
+]
+
Sent every heart-beat on the unreliable channel 0 to let agents know what the world looks like.
See State Diffs for full format and explanation, and Entity to understand the things the state contains.
Please see the list of official components. These are defined by alloplace, and define how an agent and entity interact with a place.
Please see the list of official interactions. These are defined by alloplace, and define how an agent and entity interact with a place.
HTTP service that takes a connection request, repackages as env vars, and boots the requested process. Node express.
ALLOVERSE_PLACE_URL
ALLOVERSE_REQUESTER_IDENTITY
ALLOVERSE_URL_PARAMS
Assets are delivered between agents by using the placeserv as a distribution center. Channel 2 CHANNEL_ASSETS
is used to deliver reliable messages in any direction. Messages can be requests, responses, informational or raw asset data.
“Assets” in this context means static, unchanging data used by clients or other agents, such as textures, 3d models, sound files, etc. Basically, anything that would be distributed as resource files in an app or a game.
There are no provisions for editing files, or keeping track of changes. While the asset system could be used to distribute arbitrary file systems; editing, listing and change notifications would have to be implemented as interactions.
Assets are always identified with the format:
asset:sha256:<<64 lowercase hex digits>>
The hex digits are the sha-2 256 bit hash of the full file contents of the asset itself. This means that you can’t have different versions of the same asset on different clients, because then they are in effect different assets.
No filename, mime type or any other metadata is provided in the identifier. If this turns out to be essential, we could add it as additional fields, separating fields with colons.
In general, the distribution protocol uses “pull” semantics from agents that need data. When an agent requests an asset from placeserv, it will first try to serve it from cache to the requesting agent. If none is found, it broadcasts a request to all connected agents to find who has it. Placeserv then retrieves the asset to placeserv’s cache, and then serves the asset back to the requesting agent.
The requesting agent might already have a good idea about who has the asset, and MAY include an entity id as a hint to which client SHOULD be asked first (whereafter it MUST ask all other agents to see if they have it).
In addition, if an agent has an asset that it thinks is very likely to be needed soon, it MAY push that asset immediately to the placeserv to be stored in the cache.
Once an asset is received, it MUST be checked against the hash in its identifier to verify its integrity, and discard it if the hash does not match.
Placeserv MUST function even completely without a cache. In the case of an asset response which doesn’t fit on disk, or an asset response which indicates not cacheable, placeserv must stream this asset directly to the requesting agent, keeping just enough of it in RAM to do so, whereafter it can remove the asset from memory.
On the wire, messages are:
mid
, a 16-bit unsigned big-endian integer message type code.hlen
, a 16-bit unsigned big-endian integer header length value.header
, a utf8 json body of hlen
bytes.message
, a raw bytestream of bytes, directly appended after hlen
bytes of json and until the end of the packet.Ask for an asset. The message can be:
<<mid:1>><<hlen>>{
+ "id": "<<asset id>>",
+ "range": [start_byte_offset, number_of_bytes],
+ "published_by": "<<entity id>>" // optional
+}
+
id
is the ID of the assetrange
is the byte range you want. Send [0, 0]
for a head request.published_by
is the entity ID whose owner is likely to own the asset. This is an optional field and only used as a hint. Placeserv should ask this agent first, before asking other agents.If the receiver has the asset requested, it may respond affirmative with the metadata of the asset in a “transmission header” packet. It will then commence sending “transmission chunks”.
An agent can start sending this to a placeserv (or placeserv to an agent) unprompted in case it wishes to warm up the receiver’s cache.
Once we receive start_byte_offset + number_of_bytes == total_length
the full asset has been received. The receiver should start confirming the hash of the asset before finally storing it in its cache and forwarding it to the application layer.
<<mid:2>><<hlen>>{
+ "id": "<<asset id>>",
+ "range": [start_byte_offset, number_of_bytes],
+ "total_length": <<total byte length of asset>>,
+}<<raw data>>
+
Sent in place of mid:2
in case the receiver is unable to satisfy the request (e g it doesn’t have the asset).
<<mid:3>><<hlen>>{
+ "id": "<<asset id>>",
+ "error_reason": "<<user-readable unavailability reason>>",
+ "error_code": "<<computer-reladable error code for this error>>",
+}
+
The coordinate system in Alloverse has the following properties:
Transforms are always represented using a 16-element transformation matrix. Such a 4x4 matrix is represented in data models with a column-major 16-element list of numbers.
Identity hasn’t been completely fleshed out yet, but here’s the general gist:
Sent from agent to place over unreliable channel 0 every heartbeat to indicate movement of clients. This intent is then actuated onto the client state both by client-side interpolation and on the server at the server heart rate.
Format of the packet on-wire today:
{
+ "cmd": "intent",
+ "intent": {
+ "zmovement": 0, // 1 = maximum speed forwards
+ "xmovement": 0, // 1 = maximum speed strafe right
+ "yaw": 0, // absolute rotation around x in radians
+ "pitch": 0, // absolute rotation around y in radians
+ "poses": {
+ "head": {
+ "matrix": [m11, m12, ...m44],
+ },
+ "hand/left": {
+ "matrix": [m11, m12, ...m44],
+ "skeleton": [
+ [m11, m12, ...m44],
+ [m11, m12, ...m44],
+ ...
+ ],
+ "grab": { // nil or description of grab
+ //entity id of entity being grabbed
+ "entity": "asdf",
+ // to keep hand-to-entity spatial relationship constant during grab
+ "grabber_from_entity_transform": [m11, m12, ...m44]
+ }
+ },
+ "hand/right": {same as hand/left}
+ },
+ "ack_state_rev": 1234
+ }
+}
+
For the format of the matrix in poses.*.matrix
, see coordinate system.
For hand*.skeleton
: It’s an array of matrices, each describing the pose of a node of the hand. There are 26 nodes, with each index as defined by OpenXR hand tracking. You can see a list of indexes (and node parents) in lovr’s documentation (with each index offset by 1 because lua, of course).
To understand ack_state_rev
, see (state.md)[state.md].
Planned version of the packet:
{
+ "intents": {
+ "{entity id of avatar}": {
+ (same as under "intent" in today's version)
+ },
+ "{id of other owned entity}": {
+ // same as above
+ }
+ },
+ "ack-state-rev": 73892 // sequence number of state diff
+ // we're acknowledging
+}
+
These interactions are defined by alloserv. Third party developers may create any interactions they want. They can vote to make their own interactions official by opening an issue on this repo.
After an agent connects, before it can interact with the place it must announce itself and spawn its avatar entity. Failure to announce will lead to force disconnect.
place
request
[
+ "announce",
+ "version",
+ 1,
+ "identity",
+ {
+ // identity body goes here
+ },
+ "spawn_avatar",
+ {
+ // same as "spawn_entity" key in "Agent requests to spawn entity"
+ }
+]
+
[
+ "announce",
+ "{ID of avatar entity}",
+ "{name of place}"
+]
+
TBD
place
request
[
+ "spawn_entity",
+ {
+ // list of initial values for components for new entity goes here
+
+ "children": [
+ // list of new child entities to create; same body as for "spawn_entity".
+ // These will automatically get a "relationships" component set up referencing
+ // the parent entity.
+ {
+ // list of initial values for components for child of new entity goes here
+ },
+ ...
+ ]
+ }
+]
+
[
+ "spawn_entity",
+ "{ID of entity if spawned}"
+]
+
place
request
[
+ "remove_entity",
+ "{ID of entity to remove}",
+ "{'reparent' or 'cascade'}"
+]
+
Cascade: If the removed entity has children, they will also be removed.
[
+ "remove_entity",
+ "ok"
+]
+
place
request
[
+ "change_components",
+ "{entity ID}",
+ "add_or_change",
+ {
+ // object with new values for components (regardless of if
+ // component already exists on entity)
+ }
+ "remove",
+ [
+ // keys of components to remove
+ ]
+]
+
Response:
[
+ "change_components",
+ "ok"
+]
+
A default ACL rule is set so that you must own the entity whose component you’re changing, but this rule can be changed.
place
request
[
+ "launch_app",
+ "{app URL}",
+ { launch parameters as arbitrary JSON object }
+]
+
Response:
[
+ "launch_app",
+ "ok",
+ "{avatar id}"
+]
+
or
[
+ "launch_app",
+ "error",
+ "{human-readable error string"
+]
+
Ask the Place to launch an app on behalf of the sending entity. The Place will establish a connection to the requested app if possible, launch it to that Place with the given arguments and the identity of the calling client, and then return the avatar ID if all that succeeded.
(The avatar of an app is the “main UI” entity of the app.)
An entity, most likely the avatar of a user, is pointing with their finger at another entity. This is used as a precursor to actually interacting with it (“poking it”).
The interaction describes two points in world space: the tip of the finger, and the intersection point between the ray from the finger and the nearest entity, so that the entity can know where on itself someone is pointing.
one-way
[
+ "point",
+ [1.0, 2.0, 3.0], // finger tip in world space coordinates
+ [4.0, 5.0, 6.0], // intersection point in world space coordinates
+]
+
If the ray cast from the user’s finger veers off the last pointed-at entity, one last message is sent to indicate that the user has stopped pointing at it. This is useful for removing highlight effect, etc.
[
+ "point-exit"
+]
+
Once an entity is pointing at another entity, it can ask to “physically” interact with it, by turning the pointing into a poke. The poke doesn’t contain vector information – it’s up to the receiver to correlate with pointing events, as those will be streaming a continuously updating location, while poking is a request-response which the receiver can reject. Such a rejection should be visualized, so that the sender’s user can understand if and why poking failed.
request
[
+ "poke",
+ {true|false} // whether poking started (true) or stopped (false)
+]
+
[
+ "poke",
+ "ok"
+]
+
[
+ "poke",
+ "failed",
+ "{string explaining why, presentable to user}"
+]
+
Another action an entity can do when pointing at another entity, is to “grab” it. The below interaction is sent from the visor to the grabbed entity, but the actual moving of the grabbed thing is performed with a field in the intent struct, and the movement is performed server-side. The purpose of this event is just to let the entity know that it will be moved by the server between the start and stop event, and the receiving app doesn’t have to/can’t do any movement on its own in response.
one-way
[
+ "grabbing",
+ {true|false} // whether grabbing started (true) or stopped (false)
+]
+
Ask to add an animation to an entity. See the documentation for animation descriptor in property_animations
request
[
+ "add_property_animation",
+ { animation descriptor }
+]
+
[
+ "add_property_animation",
+ "ok",
+ "abc123" // ID of this animation
+]
+
[
+ "add_property_animation",
+ "failed",
+ "{string explaining why, presentable to user}"
+]
+
Ask to remove an existing animation by ID.
[
+ "remove_property_animation",
+ "abc123"
+]
+
[
+ "remove_property_animation",
+ "ok",
+ "abc123" // ID of this animation that was removed
+]
+
[
+ "remove_property_animation",
+ "failed",
+ "{string explaining why, presentable to user}"
+]
+
Before an entity can transmit streamed audio, video or geometry, a track must be created along which to send that data. This interaction will add a live_media component to the sender’s entity.
See the live_media compponent documentation for valid media types, media formats and metadata payloads.
place
request
[
+ "allocate_track",
+ "audio", # media type
+ "opus", # media format
+ { metadata ... }
+]
+
[
+ "allocate_track",
+ "audio", # media type
+ 48000, # sample rate
+ 1, # channel count
+ "opus" # media format
+]
+
[
+ "allocate_track",
+ "ok",
+ 3 # track_id
+]
+
[
+ "allocate_track",
+ "failed",
+ "{string explaining why, presentable to user}"
+]
+
place
request
[
+ "subscribe",
+ """{guard pattern}"""
+]
+
[
+ "subscribe",
+ "{subscription ID}"
+]
+
[
+ "unsubscribe",
+ "{subscription ID}"
+]
+
[
+ "unsubscribe",
+ "{'ok'|'not_subscribed'}"
+]
+
[
+ "interaction",
+ "request",
+ "1234",
+ "place", // place is the subscription gateway
+ "567",
+ [
+ "subscribe",
+ """[
+ "new_tweet",
+ TweetSender,
+ TweetBody
+ ] where TweetSender == "nevyn" """
+ ]
+
In this scenario the place contains an app that publishes new tweets as interactions to the room. This interaction will ask the place to subscribe to all interaction publications which start with the word “new_tweet” followed by two fields. The guard clause asks that the sender must be “nevyn”. If the guard matches, the publication interaction will be forwarded by the place from the sending agent to the subscribing agent.
Every server heart-beat, the server sends the world state to all connected agents on unreliable channel 0.
The world representation is stored as a big JSON document like so:
{
+ "entities": {
+ "abc123": {
+ "id": "abc123",
+ "components": { ... }
+ }
+ },
+ "revision": 1234
+}
+
entities
: A dictionary from entity ID to entity description.revision
: An integer ID for this specific revision of the world. Monotonically increases until it reaches the biggest sequential integer that a 64-bit IEEE double can represent, which is 2^53, whereafter it will wrap around back to 0. (damn JSON).Sending this document in its entirety every server heart-beat would be needlessly wasteful, so instead only the difference to a previous known-good state is sent. An agent acknowledges successful receival of world state by setting ack_state_rev
in intent
to the received world state, whereafter the server will diff against that revision instead. Note that it takes time for this ACK to be received, so you might continue to receive diffs against an older version; so you need to keep a history of previous states to apply diffs to.
The packet on channel 0 received in the client can take three forms:
set
If the server is unable to diff, it will just send the full world state document.
{
+ "patch_style": "set",
+ "entities": ...,
+ "revision": 1234
+}
+
Note that patch_style
is not part of the document, and should be removed before the document is stored to history.
apply
Indicates that the payload is an RFC 6902 JSON Patch.
{
+ "patch_style": "apply",
+ "patch_from": 1233,
+ "patches": [ ... /*RFC 6902 patches*/ ]
+}
+
patch_from
indicates the revision to apply the patches topatches
is a JSON list of RFC 6902 patches to apply to said revision from historyThe patches should be applied and the resulting document stored to history. If the referenced from
revision is not available or history, or if patching fails, you should set intent’s ack_state_rev
back to 0 to request a full set
state.
Note: This format seems to almost always be less efficient than RFC 7386, so its support is likely to be removed.
merge
Indicates that the payload is an RFC 7386 JSON Merge Patch.
{
+ "patch_style": "merge",
+ "patch_from": 1233,
+ ... // rest of document is an RFC 7386 payload
+}
+
patch_style
and patch_from
must be removed from the document before it’s a valid RFC 7386 merge patch.patch_from
indicates the revision to apply the patches toThe merge patch should be applied and the resulting document stored to history. If the referenced from
revision is not available or history, or if patching fails, you should set intent’s ack_state_rev
back to 0 to request a full set
state.
Version 1 sent the complete world-state as is, and with entities as an array rather than an object. It looked like this:
entities: [
+ // list of all entities; see Entities above for structure
+],
+revision: 1234 // monotonically increasing integer (will roll over to 0 after INT64_MAX!)
+
URLs are used to identify and locate various resources in Alloverse. This document should be a full list of such URLs and their format.
There is currently one format for locating an alloplace server:
alloplace://{host}:{port}
host
: Hostname or IP of machine that is running the alloplace
serviceport
: This is the enet UDP endpoint for the listen socket. Optional: default value is 21337.Such an URL should be fed into alloclient_connect
or its high-level equivalents, and allonet
will take care of parsing and connecting to the server at that URL.
The downside of this scheme is that it doesn’t allow for virtual hosting: with the same domain name for the same IP, you’ll end up at the same placeserv unless you include a custom port in the URL, which is ugly.
One solution is to use a HTTPS gateway which returns the real alloplace URL given a URL of this form:
alloplacegw:http(s)://{host}:{port}/{path}
Another solution is to have a “load balancer” allplace socket which forwards to an internal instance. This solution can use the original alloplace://
url schema.
An alloapp is launched with an URL in this format:
alloapp:http(s)://{host}:{port}/{path}?{params}
This is a HTTP(S) gateway endpoint which is basically a “CGI launcher”. Upon POST, it’ll launch an instance of the app and connect it to the requested alloplace. Please read more over on the topic of hosting.
You can launch this gateway by calling ./allo/assist serve
inside an alloapp.
When an alloplace accesses the above URL, it will fill in these HTTP headers:
x-alloverse-server
: The alloplace:
url of the place that the new app instance shouild connect tox-alloverse-launched-by
: The identity blob of the user who requested the app launchWhat are the actual bytes travelling on the wire? They all seem to be json payload followed by a newline character…
<<JSON-payload>>\n
+
<<JSON-payload>>\n
+
<<JSON-payload>>\n
+
Version 0 implements this packet as:
{
+ "cmd": "interact",
+ "interact": {
+ "from_entity": "...",
+ "to_entity": "...",
+ "cmd": "..."
+ }
+}\n
+
<<JSON-payload>>\n
+
<<track-id as 32bit big endian integer>><<raw media payload>>
+
Note that in older versions of the protocol, even audio/media packets had that stupid newline at the end.
{
+ "client_clock": 123.0,
+ "server_clock": 456.0
+}\n
+
This packet is sent from the client once a second with only the client_clock
field set to its local monotonic clock time. It is immediately bounced back from the server, with its server_clock
set.
The client can thus calculate its round-trip latency (now() - response["client_clock"]
), and delta between its clock and the server clock. Using all this information, it can then extrapolate the world time and update place
’s clock
component.