Check out our latest project ✨ OpenChapter.io: free ebooks the way its meant to be 📖

Lynx Relay

An asset by AdamKormos34
The page banner background of a mountain and forest
Lynx Relay hero image

Quick Information

0 ratings
Lynx Relay icon image
AdamKormos34
Lynx Relay

Lynx lets games contribute to the same shared event, unlock global milestones, and reward players when the community reaches a goal.

Supported Engine Version
4.0
Version String
1.0.0
License Version
MIT
Support Level
community
Modified Date
6 hours ago
Git URL
Issue URL

Lynx

Website

Discord Community

Lynx is a Godot plugin for shared community events. (Compatible with 4.0 and above)

Players contribute to a global online event counter from inside your game.
When the community reaches a goal, your game can unlock rewards for players.

Example:

All players collect coins together.
The shared goal reaches 1000.
A reward chest becomes available in every player's game.

Lynx is built for indie games that want lightweight online community events without building a full custom backend from scratch.


Features

  • Shared online event counters
  • Simple autoload API
  • Reward availability detection
  • Safe reward claim flow
  • LynxHandler helper interface for handling event UI, rewards, and player interactions
  • Global popup UI for event updates and reward notifications
  • Dynamic goals with growth multipliers
  • Example 2D project

Status

Lynx is currently in alpha / early access.

APIs and backend behavior may still change. Use it for prototypes, tests, game jams, and early integrations.


Installation & Setup

0. Get your game registered

The API regulates usage to verified games, using an identifier system. To register your game, join our Discord community and see the instructions in the #how-to-join channel.

1. Download the repository and add the res://addons/lynx folder into your Godot project, to the same folder path:

Screenshot_1

2. Enable the plugin inside Godot:

Project -> Project Settings -> Plugins -> Lynx -> Enable

The plugin automatically adds these autoloads:

LynxRelay
LynxRewards
LynxEventPopup

3. Configure your game identifiers:

After registering your game to Lynx, you receive a public Game ID, a runtime Game Key, and a private Game Admin Password. Put only the Game ID and Game Key in the Godot project. Keep the Game Admin Password out of exported builds and public repositories. Open the Lynx client script and update the following variables:

const GAME_ID := "your-game-id"
const GAME_KEY := "your-game-key"

Do not commit a real Game Admin Password to a public repository. The Game Key is a runtime credential, not an admin password, but you can still rotate it from the admin tools if it is abused.

For most games, the easiest way to use Lynx is through LynxHandler.

LynxHandler is a small interface node that connects the Lynx signals for you and gives you simple functions to override in your own game.

Use it to handle:

  • event counter UI updates
  • player contributions
  • reward availability
  • reward object spawning
  • reward claim start / success / failure

An example use case can be seen in the sample 2D project, available within this repository's res://addons/lynx/Example 2D Project. Instead of wiring every signal manually, extend LynxHandler and override the parts your game needs:

extends LynxHandler


@onready var counter_label: Label = %CounterLabel
@onready var reward_chest: Node2D = %RewardChest


func update_goal_state_visuals(relay_state: Dictionary) -> void:
    var value := int(relay_state.get("value", 0))
    var target := int(relay_state.get("target", 0))

    counter_label.text = str(value) + "/" + str(target)


func display_reward_item(offer: Dictionary) -> void:
    reward_chest.visible = true


func on_player_reward_claim_success() -> void:
    reward_chest.visible = false

    # Give your reward here.
    # Example:
    # player_inventory.add_item("community_chest")

Set which event the handler listens to:

@export var listened_event: String = "sunfall"

LynxHandler is the recommended starting point. You can still use LynxRelay and LynxRewards directly if you want full control.


Requirements in integration

After your game is verified, you receive your game credentials. We ask you to keep the following requirements in mind:

  • You will represent at least one event in your game, and grant any kind of reward per goal (doesn't need to make the player "powerful" per se, anything is good!).
  • The UI popup (read more below) displays correctly in your game, both in case of the player contributing to the goal, and the reward becoming available - but you are free to customize the layout and appearance of this box.
  • Your game supports the ability to open a hyperlink of each event counter, where participating games of the event are also listed. This hyperlink button is available on the UI popup by clicking, but you can bind a keybind to it otherwise.
  • If you're exporting to Android, make sure to enable Internet permissions on your export settings! If this is not done, the plugin cannot function.
  • Do not spam or abuse any events. If you do so, your access will be revoked. This is just a common sense rule - don't push an increment every game frame, don't spam the server etc. The plugin code already handles streamlined communication.
  • Rewards are claimed using highest-tier policy (described under the Claiming a Reward section). This just means you don't modify the existing plugin code to behave otherwise.

Basic Usage

Add progress to an event

Call this when the player does something that should contribute to the community goal:

await LynxRelay.increment_relay("sunfall", 1)

Example:

func _on_coin_collected() -> void:
    await LynxRelay.increment_relay("sunfall", 1)

Reading Event Progress

Sync the event state:

var result := await LynxRelay.sync_relay("sunfall")

if result.get("ok", false):
    var state: Dictionary = result.get("relay_state", {})
    var value := int(state.get("value", 0))
    var target := int(state.get("target", 0))

    print(value, "/", target)

Example UI:

counter_label.text = str(value) + "/" + str(target)

Listening for Updates Manually

You can also connect to Lynx signals yourself:

func _ready() -> void:
    LynxRelay.sync_completed.connect(_on_sync_completed)
    LynxRelay.increment_completed.connect(_on_increment_completed)
    LynxRewards.offer_available.connect(_on_reward_available)
func _on_sync_completed(relay_id: String, relay_state: Dictionary, claim: Dictionary) -> void:
    if relay_id != "sunfall":
        return

    var value := int(relay_state.get("value", 0))
    var target := int(relay_state.get("target", 0))

    counter_label.text = str(value) + "/" + str(target)

Rewards

When a community goal is reached, Lynx can create a reward offer.

Listen for it:

func _ready() -> void:
    LynxRewards.offer_available.connect(_on_reward_available)
func _on_reward_available(relay_id: String, offer: Dictionary) -> void:
    if relay_id != "sunfall":
        return

    reward_chest.visible = true

With LynxHandler, you usually only need to override display_reward_item():

func display_reward_item(offer: Dictionary) -> void:
    reward_chest.visible = true

Claiming a Reward

When the player interacts with your reward object:

func claim_reward() -> void:
    if not LynxRewards.has_offer("sunfall"):
        return

    var result := await LynxRewards.prepare_offer_claim("sunfall")

    if result.get("superseded", false):
        return

    if not result.get("ok", false):
        print("Reward claim failed: ", result)
        return

    # Give your reward here.
    # Example:
    # player_inventory.add_item("community_chest")

    # Save your game before completing the offer.
    # SaveGame.save()

    LynxRewards.complete_offer("sunfall")

The important order is:

prepare -> give reward -> save game -> complete

This helps avoid duplicate or lost rewards.

Lynx currently follows a policy of granting the highest-tier reward only. Assume that a player claimed the reward after reaching the goal for the 1st time, but they don't play for a while. Now, they run the game again and even the 3rd goal has been reached. In this case, this player only claims the 3rd goal's reward. Players have no access to rewards prior to their first play session.

If you use LynxHandler, the sample handler already follows this flow and calls:

on_player_reward_claim_start()
on_player_reward_claim_success()
on_player_reward_claim_fail()

Override those functions to connect Lynx reward claiming to your own game logic.


Lynx includes a global popup UI for event updates and reward notifications.

It can show when:

  • the local player contributes to an event counter
  • a community reward becomes available
  • a reward is claimed

Example calls:

LynxEventPopup.show_counter_contribution("sunfall", 1, relay_state)
LynxEventPopup.show_reward_available("sunfall", offer)
LynxEventPopup.show_reward_claimed("sunfall", offer)

The popup is a normal Godot scene, so you can restyle or replace it for your own game.

The default popup uses:

CanvasLayer
AnimationPlayer
RichTextLabel for event name
RichTextLabel for event progress

The default animation name is:

Display

The Display animation should handle the full visual movement:

enter -> stay -> exit

Do not animate the text property of the popup labels, otherwise the animation may overwrite the text set by the script.


Example Project

The repository includes a small 2D example showing:

  • contributing to an event
  • displaying progress
  • using LynxHandler
  • showing reward availability
  • claiming a reward
  • using the popup UI

Look in:

addons/lynx/Example 2D Project/

Dynamic Goals

Lynx supports growing goals.

Example:

Goal 1: 100
Goal 2: 220
Goal 3: 364

This lets community events become harder over time.

The Godot plugin automatically receives the current target from the backend.


Security Notes

Do not publish:

  • admin tokens
  • backend secrets
  • private API keys
  • Cloudflare secrets
  • payment keys

Game Keys are runtime credentials and may be included in game builds, but public examples should use placeholders.

Game Admin Passwords must never be included in game clients, exported builds, public repositories, screenshots, logs, or support messages.


Roadmap

Planned improvements:

  • easier editor configuration
  • better sample scenes
  • hosted dashboard
  • more popup themes
  • Godot Asset Library release
  • improved backend documentation

License

MIT License.


Credits

Created by Ádám Kormos.

Lynx lets games contribute to the same shared event, unlock global milestones, and reward players when the community reaches a goal.

Reviews

0 ratings

Your Rating

Headline must be at least 3 characters but not more than 50
Review must be at least 5 characters but not more than 500
Please sign in to add a review

Quick Information

0 ratings
Lynx Relay icon image
AdamKormos34
Lynx Relay

Lynx lets games contribute to the same shared event, unlock global milestones, and reward players when the community reaches a goal.

Supported Engine Version
4.0
Version String
1.0.0
License Version
MIT
Support Level
community
Modified Date
6 hours ago
Git URL
Issue URL

Open Source

Released under the AGPLv3 license

Plug and Play

Browse assets directly from Godot

Community Driven

Created by developers for developers