Skip to content

Commit 9351447

Browse files
committed
update for v0.1
1 parent c1e9e87 commit 9351447

File tree

4 files changed

+213
-7
lines changed

4 files changed

+213
-7
lines changed

docs/ads.md

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
Nostr Game Engine supports **native in-game advertising** through its integration with the [nostrads](https://nostr-ads.ngengine.org/) protocol.
2+
3+
4+
5+
## Preparing the App
6+
7+
8+
Before enabling ads, make sure your application is initialized with a valid `appId` (see [NGEApplication](../getting-started/#ngeapplication)).
9+
10+
The `appId` must correspond to a pubkey that has a valid metadata event containing a **lnurl** or **lightning address** (`lud06` or `lud16`) for receiving ad revenue.
11+
12+
The easiest way to set this up is by using a Nostr client such as [Primal](https://primal.net/). In your profile settings, you’ll find a field where you can set your lightning address:
13+
14+
![Setting a lightning address in Primal](./images/primal-lud16.webp)
15+
16+
If you don’t yet have a lightning address for your app, here are some options (from simplest to more advanced):
17+
18+
1. [Blink.sv](https://www.blink.sv/) - custodial mobile wallet that provides a lightning address.
19+
2. [Rizful](https://rizful.com/) - offers both custodial and non-custodial wallets with lightning addresses.
20+
3. [AlbyHub](https://albyhub.com/) - self-hosted non-custodial lightning node with a lightning address.
21+
4. [LNbits](https://lnbits.com/) - self-hosted interface that connects to various Lightning backends and can provide a lightning address via extensions.
22+
23+
24+
## Enabling Ads
25+
26+
To enable ads in your app, call the `enableAds` convenience method on `NGEApplication`:
27+
28+
```java
29+
NGEAppRunner appRunner = NGEApplication.createApp(appId, settings, app -> {
30+
// ... callback once app is ready ....
31+
app.enableAds();
32+
// ...
33+
});
34+
```
35+
36+
This will automatically:
37+
38+
* Add an `ImmersiveAdComponent`
39+
* Initialize it with a default set of relays
40+
* Create a random **advertisement key** (an anonymous private key used to identify the current player in the ads network)
41+
42+
If you want to use a custom set of relays and/or a custom advertisement key, you can pass them explicitly:
43+
44+
```java
45+
NostrPrivateKey adsKey = NostrPrivateKey.generate();
46+
app.enableAds(adsKey, List.of("wss://relay.ngengine.org", "wss://relay2.ngengine.org"));
47+
```
48+
49+
!!! note
50+
How you generate the `adsKey` is up to you.
51+
52+
- You may generate a new random key for each play session.
53+
- Or you may persist it across sessions.
54+
55+
56+
In either case, **never link it directly to the player’s identity**, to preserve privacy.
57+
58+
59+
60+
61+
## Setting Up the Scene for Ad Spaces
62+
63+
Next, you need to prepare your scene to detect ad spaces and display ads.
64+
This is done by adding an `ImmersiveAdControl` to the `rootNode` (or any other spatial in your scene):
65+
66+
```java
67+
ImmersiveAdControl adControl = new ImmersiveAdControl(assetManager);
68+
map.addControl(adControl);
69+
```
70+
71+
Once added, register the control with the `ImmersiveAdComponent`:
72+
73+
```java
74+
componentManager.getComponent(ImmersiveAdComponent.class).register(adControl);
75+
```
76+
77+
---
78+
79+
### Context-Aware Advertising and Ad Groups
80+
81+
The setup above is enough to start showing ads, but it won’t filter them by context or preferences.
82+
For that, use the extended `ImmersiveAdControl` constructor:
83+
84+
```java
85+
public ImmersiveAdControl(
86+
@Nonnull AssetManager assetManager,
87+
@Nullable List<AdTaxonomy.Term> categoryIds,
88+
@Nullable List<String> languages,
89+
@Nullable AdPriceSlot priceSlot,
90+
@Nullable String context // unused for now
91+
) {}
92+
```
93+
94+
!!! tip
95+
You can obtain `AdTaxonomy.Term` instances either by:
96+
97+
**Creating a new taxonomy instance**:
98+
```java
99+
AdTaxonomy taxonomy = new AdTaxonomy();
100+
```
101+
102+
**Or retrieving it from the component**:
103+
```java
104+
AdTaxonomy taxonomy = componentManager
105+
.getComponent(ImmersiveAdComponent.class)
106+
.getTaxonomy();
107+
```
108+
109+
Then, fetch terms by ID or path:
110+
111+
```java
112+
Term term = taxonomy.getById("150");
113+
Term term = taxonomy.getByPath("Attractions");
114+
```
115+
116+
For a full list of taxonomy IDs and paths, see the [Nostr Content Taxonomy CSV](https://ngengine.org/docs/nip-drafts/nostr-content-taxonomy/).
117+
118+
119+
#### Multiple Contexts in the Game World
120+
121+
An `ImmersiveAdControl` only affects the spatial it’s attached to and its subtree.
122+
123+
* If attached to the `rootNode`, it applies to the entire scene.
124+
* You can attach multiple controls to different children of the `rootNode`, each with different filters, allowing different ads in different areas of your game world.
125+
126+
---
127+
128+
#### Advanced Filtering
129+
130+
The `ImmersiveAdControl` also supports a custom filter via:
131+
132+
```java
133+
adControl.setFilter((AdBidEvent event) -> {
134+
// return true to accept, false to reject
135+
});
136+
```
137+
138+
This lets you implement complex **client-side filtering** logic, tailored to your app’s needs.
139+
140+
141+
142+
## Adding Adspaces to 3D Models
143+
144+
**Adspaces** are 3D surfaces in your game world where ads can be displayed.
145+
146+
The `ImmersiveAdControl` automatically detects adspaces, applies the correct material, and loads ads as textures.
147+
However, you still need to **define the geometry** that will host each adspace.
148+
149+
150+
### Defining Adspaces Programmatically
151+
152+
You can create adspaces directly in code by adding a `Geometry` of any shape (commonly `Quad` or `Box`) and tagging it with the supported resolution:
153+
154+
```java
155+
Geometry adspace = new Geometry("AdQuad", new Quad(2, 4));
156+
adspace.setUserData("nostrads.adspace", "256x512");
157+
```
158+
159+
Here, `"256x512"` is one of the supported resolutions defined by the nostrads protocol.
160+
A full list of valid sizes can be found in the [nostrads documentation](http://localhost:8000/docs/nip-drafts/nip-ADS/#ad-size-and-aspect-ratio) (`s` tag).
161+
162+
!!! note
163+
You must ensure the geometry’s **aspect ratio** matches the chosen ad size.
164+
Otherwise, the ad will appear stretched or distorted.
165+
166+
167+
### Defining Adspaces in a 3D Editor (Recommended)
168+
169+
The preferred approach is to define adspaces directly in your 3D modeling tool (e.g. **Blender**).
170+
171+
1. Create a geometry (e.g. a cube or plane) with the correct aspect ratio.
172+
2. Add the custom property:
173+
- Key: `nostrads.adspace`
174+
- Value: one of the supported resolutions (e.g. `256x512`).
175+
176+
Resolutions must match the supported sizes defined by the nostrads protocol, in the [nostrads documentation](http://localhost:8000/docs/nip-drafts/nip-ADS/#ad-size-and-aspect-ratio) (`s` tag).
177+
178+
!!! tip
179+
In Blender, when exporting to **glTF**, make sure to enable:
180+
`Include → Custom Properties`
181+
This ensures your `nostrads.adspace` property is exported correctly.
182+

docs/getting-started.md

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,36 @@ You can start experimenting with the source code you've just downloaded, to see
7373
Whenever your application is ready, you can head over to the [Building and Distribution](./build.md) section to learn how to package your app for distribution.
7474

7575

76+
77+
7678
## NGEApplication
7779

78-
Everything begins with a `NGEApplication`, which you instantiate via a static method:
80+
Everything begins with an `NGEApplication`.
81+
An `NGEApplication` should be initialized with an **appId**, which is simply a valid Nostr public key.
82+
83+
The **appId** is used throughout the engine to uniquely identify your application. For example, it scopes persistent storage data and serves as your app’s identifier for [in-game ads](./ads.md).
84+
85+
You can generate an appId in two ways:
86+
87+
* **Generate a random key**
7988

8089
```java
81-
Runnable appBuilder = NGEApplication.createApp(app -> {
82-
// ...
90+
NostrPublicKey appId = NostrPrivateKey.generate().getPublicKey();
91+
```
92+
93+
* **Import an existing key**
94+
That can be obtained, for example, by creating a Nostr profile with a client like [Primal](https://primal.net/).
95+
This approach is recommended because it allows you to easily compile a complete app profile, which will be used later in the documentation.
96+
97+
```java
98+
NostrPublicKey appId = NostrPublicKey.fromBech32("npub...");
99+
```
100+
101+
Once you have your appId, you can initialize your application with:
102+
103+
```java
104+
NGEAppRunner appRunner = NGEApplication.createApp(appId, app -> {
105+
// ... callback once app is ready ....
83106
});
84107
```
85108

@@ -105,17 +128,17 @@ settings.setVSync(true);
105128
settings.setGraphicsDebug(false);
106129
settings.setTitle("Nostr Game Engine Demo");
107130

108-
Runnable appBuilder = NGEApplication.createApp(settings, app -> {
109-
// ...
131+
Runnable appBuilder = NGEApplication.createApp(appId, settings, app -> {
132+
// ... callback once app is ready ....
110133
});
111134
```
112135

113136

114137
## Initialize and Run!
115-
Once you have the `Runnable` returned from `createApp`, you can call:
138+
Once you have the `NGEAppRunner` returned from `createApp`, you can call:
116139

117140
```java
118-
appBuilder.run();
141+
appBuilder.start();
119142
```
120143

121144
to begin initializing and running your application.

docs/images/primal-lud16.webp

46.2 KB
Loading

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ nav:
6161
- Graphical User Interface (GUI):
6262
- NGE Simple UI: gui/index.md
6363
- Lemur: gui/lemur.md
64+
- In-Game Advertising: ads.md
6465
- Javadoc: https://javadoc.ngengine.org/
6566
- jMonkeyEngine Wiki: https://wiki.jmonkeyengine.org
6667
- NIP Drafts: nip-drafts/index.md

0 commit comments

Comments
 (0)