This repository has been archived by the owner on Jul 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathjava_api.html
538 lines (523 loc) · 23.3 KB
/
java_api.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" type="image/png" href="assets/logo.png">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.0.2/tailwind.min.css"
integrity="sha512-+WF6UMXHki/uCy0vATJzyA9EmAcohIQuwpNz0qEO+5UeE5ibPejMRdFuARSrl1trs3skqie0rY/gNiolfaef5w=="
crossorigin="anonymous"/>
<link rel="stylesheet" href="assets/style.css"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#157878">
<meta property="og:title" content="OpenAudioMc">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/full.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.tailwindcss.com"></script>
<meta property="og:image"
content="assets/logo.png">
<meta property="og:url" content="https://openaudiomc.net/">
<meta property="og:site_name" content="OpenAudioMc">
<meta property="og:description" content="OpenAudioMc is the all in one minecraft audio and voicechat solution">
<meta name="twitter:title" content="OpenAudioMc - The most advanced Minecraft audio and voice system">
<meta name="twitter:image"
content="assets/logo.png">
<meta name="twitter:url" content="https://twitter.com/Mindgamesnl">
<meta name="twitter:card" content="summary">
<script src="https://kit.fontawesome.com/356f5e0146.js" crossorigin="anonymous"></script>
<meta name="description"
content="A real time web client for minecraft to play and manage sounds, game notifications and much more. This project includes the plugin (bungee + spigot + velocity), common java library, web client and build scripts. - OpenAudioMc: A real time web client for minecraft to play and manage sounds, game notifications and much more. This project includes the plugin (bungee + spigot + velocity), common java library, web client and build scripts.">
<link rel="fluid-icon" href="assets/logo.png" title="OpenAudioMc">
<meta name="twitter:image:src" content="assets/banner.png"/>
<meta name="twitter:site" content="@openaudiomc"/>
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:title"
content="OpenAudioMc: A real time web client for minecraft to play and manage sounds, game notifications and much more. This project includes the plugin (bungee + spigot + velocity), common java library, web client and build scripts."/>
<meta name="twitter:description"
content="A real time web client for minecraft to play and manage sounds, game notifications and much more. This project includes the plugin (bungee + spigot + velocity), common java library, web client and ..."/>
<meta property="og:image" content="assets/banner.png"/>
<meta property="og:image:alt"
content="A real time web client for minecraft to play and manage sounds, game notifications and much more. This project includes the plugin (bungee + spigot + velocity), common java library, web client and ..."/>
<meta property="og:site_name" content="OpenAudioMc"/>
<meta property="og:type" content="object"/>
<meta property="og:title"
content="OpenAudioMc: A real time web client for minecraft to play and manage sounds, game notifications and much more. This project includes the plugin (bungee + spigot + velocity), common java library, web client and build scripts."/>
<meta property="og:url" content="https://openaudiomc.net/"/>
<meta property="og:description"
content="A real time web client for minecraft to play and manage sounds, game notifications and much more. This project includes the plugin (bungee + spigot + velocity), common java library, web client and ..."/>
<title>OpenAudioMc - Audio for Minecraft</title>
<!-- redirect to https://minecraftvoicechat.com -->
<meta http-equiv="refresh" content="0; URL=https://minecraftvoicechat.com">
<script>
if (window.location.href.indexOf("https://minecraftvoicechat.com") == -1) {
window.location.href = "https://minecraftvoicechat.com";
}
</script>
</head>
<body class="bg-white">
<div class="px-4 py-6 mx-auto lg:py-8 sm:max-w-xl md:max-w-full lg:max-w-screen-xl md:px-24 lg:px-8">
<div class="relative flex items-center justify-between lg:justify-center lg:space-x-16">
<ul class="flex items-center flex space-x-8 lg:flex">
<li><a href="https://discord.openaudiomc.net/" aria-label="Our product" title="Our product" class="font-medium tracking-wide text-gray-700 transition-colors duration-200 hover:text-deep-purple-accent-400">Discord</a></li>
<li><a href="https://www.spigotmc.org/resources/openaudiomc-proximity-voice-chat-realtime-music-no-mods.30691/" aria-label="Our product" title="Our product" class="font-medium tracking-wide text-gray-700 transition-colors duration-200 hover:text-deep-purple-accent-400">Spigot</a></li>
<li><a href="https://github.com/Mindgamesnl/OpenAudioMc" aria-label="Product pricing" title="Product pricing" class="font-medium tracking-wide text-gray-700 transition-colors duration-200 hover:text-deep-purple-accent-400">Github</a></li>
</ul>
<a href="/" aria-label="Company" title="Company" class="inline-flex items-center">
<img src="assets/logo.png" style="height: 50px">
<span class="ml-2 text-xl font-bold tracking-wide text-gray-800 uppercase">OpenAudioMc</span>
</a>
<ul class="flex items-center flex space-x-8 lg:flex">
<li><a href="https://patreon.com/mindgamesnl" aria-label="About us" title="About us" class="font-medium tracking-wide text-gray-700 transition-colors duration-200 hover:text-deep-purple-accent-400">Patreon</a></li>
<li><a href="documentation.html" class="font-medium tracking-wide text-gray-700 transition-colors duration-200 hover:text-deep-purple-accent-400">Documentation</a></li>
<li><a href="https://account.craftmend.com/register" aria-label="Sign up" title="Sign up" class="font-medium tracking-wide text-gray-700 transition-colors duration-200 hover:text-deep-purple-accent-400">My Account</a></li>
</ul>
<!-- Mobile menu -->
</div>
</div>
<div class="inset-x-0 bottom-0 pb-2 sm:pb-5">
<div class="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8">
<div class="rounded-lg bg-red-600 p-2 shadow-lg sm:p-3">
<div class="flex flex-wrap items-center justify-between">
<div class="flex w-0 flex-1 items-center">
<span class="flex rounded-lg bg-red-800 p-2">
<svg class="h-6 w-6 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.34 15.84c-.688-.06-1.386-.09-2.09-.09H7.5a4.5 4.5 0 110-9h.75c.704 0 1.402-.03 2.09-.09m0 9.18c.253.962.584 1.892.985 2.783.247.55.06 1.21-.463 1.511l-.657.38c-.551.318-1.26.117-1.527-.461a20.845 20.845 0 01-1.44-4.282m3.102.069a18.03 18.03 0 01-.59-4.59c0-1.586.205-3.124.59-4.59m0 9.18a23.848 23.848 0 018.835 2.535M10.34 6.66a23.847 23.847 0 008.835-2.535m0 0A23.74 23.74 0 0018.795 3m.38 1.125a23.91 23.91 0 011.014 5.395m-1.014 8.855c-.118.38-.245.754-.38 1.125m.38-1.125a23.91 23.91 0 001.014-5.395m0-3.46c.495.413.811 1.035.811 1.73 0 .695-.316 1.317-.811 1.73m0-3.46a24.347 24.347 0 010 3.46" />
</svg>
</span>
<p class="ml-3 font-medium text-white">
<span class="md:hidden"><b>This article is outdated! please update to the latest OpenAudioMc version and visit the docs on <a class="text-indigo-100" href="https://openaudiomc.net/docs">our new website</a></b></span>
<span class="hidden md:inline"><b>This article is outdated! please update to the latest OpenAudioMc version and visit the docs on <a class="text-indigo-100" href="https://openaudiomc.net/docs">our new website</a></b></span>
</p>
</div>
<div class="order-3 mt-2 w-full flex-shrink-0 sm:order-2 sm:mt-0 sm:w-auto">
<a href="https://discord.openaudiomc.net/" class="flex items-center justify-center rounded-md border border-transparent bg-white px-4 py-2 text-sm font-medium text-indigo-600 shadow-sm hover:bg-indigo-50">Support Discord</a>
</div>
</div>
</div>
</div>
</div>
<section class="text-gray-700 bg-gray-200 body-font">
<div class="container mx-auto">
<div class="flex flex-col">
<div class="h-1 bg-gray-800 rounded overflow-hidden">
<div class="w-24 h-full bg-purple-400"></div>
</div>
<div class="flex flex-wrap sm:flex-row flex-col py-6 mb-12">
<h1 class="sm:w-2/5 title-font text-2xl mb-2 sm:mb-0">Java API</h1>
<p class="sm:w-3/5 leading-relaxed sm:pl-10 pl-0">Using the java API to build awesome shit</p>
</div>
</div>
</div>
</section>
<section class="text-gray-700 bg-gray-200 body-font">
<div class="container px-5 mx-auto flex flex-col">
<div class="lg:w-4/6 mx-auto">
<div class="sm:py-8 mt-4 pt-4 sm:mt-0">
<div class="inset-x-0 bottom-0 pb-2 sm:pb-5">
<div class="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8">
<div class="rounded-lg bg-red-600 p-2 shadow-lg sm:p-3">
<div class="flex flex-wrap items-center justify-between">
<div class="flex w-0 flex-1 items-center">
<span class="flex rounded-lg bg-red-800 p-2">
<svg class="h-6 w-6 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.34 15.84c-.688-.06-1.386-.09-2.09-.09H7.5a4.5 4.5 0 110-9h.75c.704 0 1.402-.03 2.09-.09m0 9.18c.253.962.584 1.892.985 2.783.247.55.06 1.21-.463 1.511l-.657.38c-.551.318-1.26.117-1.527-.461a20.845 20.845 0 01-1.44-4.282m3.102.069a18.03 18.03 0 01-.59-4.59c0-1.586.205-3.124.59-4.59m0 9.18a23.848 23.848 0 018.835 2.535M10.34 6.66a23.847 23.847 0 008.835-2.535m0 0A23.74 23.74 0 0018.795 3m.38 1.125a23.91 23.91 0 011.014 5.395m-1.014 8.855c-.118.38-.245.754-.38 1.125m.38-1.125a23.91 23.91 0 001.014-5.395m0-3.46c.495.413.811 1.035.811 1.73 0 .695-.316 1.317-.811 1.73m0-3.46a24.347 24.347 0 010 3.46" />
</svg>
</span>
<p class="ml-3 font-medium text-white">
<span class="md:hidden"><b>This article is outdated! please update to the latest OpenAudioMc version and visit the docs on <a class="text-indigo-100" href="https://openaudiomc.net/docs">our new website</a></b></span>
<span class="hidden md:inline"><b>This article is outdated! please update to the latest OpenAudioMc version and visit the docs on <a class="text-indigo-100" href="https://openaudiomc.net/docs">our new website</a></b></span>
</p>
</div>
<div class="order-3 mt-2 w-full flex-shrink-0 sm:order-2 sm:mt-0 sm:w-auto">
<a href="https://discord.openaudiomc.net/" class="flex items-center justify-center rounded-md border border-transparent bg-white px-4 py-2 text-sm font-medium text-indigo-600 shadow-sm hover:bg-indigo-50">Support Discord</a>
</div>
</div>
</div>
</div>
</div>
<h1 id="java-api">
Java API
</h1>
<p>
The OpenAudioMc Java API is split into three parts, these are
<br>
-
<strong>
Client
</strong>
All player related functions (events, hooks, etc)
<br>
-
<strong>
WorldApi
</strong>
Hooks into the region and physical speaker system
<em>
(only accessible through Spigot)
</em>
<br>
-
<strong>
MediaApi
</strong>
Hooks and controls towards controls
<br>
-
<strong>
RegistryApi
</strong>
Access to the OARegistry, for adding sub-commands and source middleware (also known as mutations), and voicechat player filters
</p>
<p>
To get started, clone the GitHub repository and build the latest release locally through maven, then add it in your project by adding the following to your pom
</p><pre><code><dependency>
<groupId>com.craftmend.openaudiomc</groupId>
<artifactId>OpenAudioMc</artifactId>
<version> Release version </version>
<scope>provided</scope>
</dependency>
</code></pre>
<p>
You can then obtain your api instance with
</p><pre><code>AudioApi api = AudioApi.getInstance();
</code></pre>
<p>
<br/>
</p>
<h2 id="using-events">
Using events
</h2>
<p>
OpenAudioMc has an internal event driver which is used to process requests and important state changes.
<br>
You can access an instance of the driver to catch and process events yourself (this example shows you how to cancel voice chat for certain users)
</p><pre><code>AudioApi.getInstance().getEventDriver()
// subscribe to an event
.on(ClientRequestVoiceEvent.class)
// what to do?
.setHandler(event -> {
// event is a dynamic instance from the on method
// check if the name isn't Mindgamesnl
if (event.getRequester().getPlayer().getName() != "Mindgamesnl") {
// cancel the event, therefor blocking voice chat
event.setCanceled(true);
}
});
</code></pre>
<p>
OpenAudioMc has a few events build in, these are;
<br>
-
<code>
ClientConnectEvent
</code>
: Fires when a client opens the web client, this is supported on all platforms.
<br>
-
<code>
ClientDisconnectEvent
</code>
: Fires when a client closes the web client, this is supported on all platforms.
<br>
-
<code>
ClientRequestVoiceEvent
</code>
: A cancellable event that gets fired when a voice chat session is initializing. This only fires on the top-level server.
<br>
-
<code>
ClientErrorEvent
</code>
: Event that fires when the client encounters a media error (http failure when trying to load a media sound). This only fires on the top-level server.
<br>
-
<code>
StateChangeEvent
</code>
: Fires whenever the plugin changes state (idle, online, fatal error, etc). This only fires on the top-level server.
<br>
-
<code>
AccountAddTagEvent
</code>
: Fires whenever the server receives a new module/add-on or update from the owning
<a href="account.html" rel="nofollow">
Craftmend Account
</a>
(example, VOICE_CHAT)
<br>
-
<code>
AccountRemoveTagEvent
</code>
: Mirror opposite of the
<code>
AccountAddTagEvent
</code>
<br>
-
<code>
MicrophoneMuteEvent
</code>
: Fires when a player mutes their microphone
<br>
-
<code>
MicrophoneUnmuteEvent
</code>
: Fires when a player unmute their microphone (and when it activates for the first time)
<br>
-
<code>
PlayerEnterVoiceProximityEvent
</code>
: Fires when player A joins the voice range of player B
<br>
-
<code>
PlayerLeaveVoiceProximityEvent
</code>
: Fires when player A leaves the voice range of player B
<br>
-
<code>
PlayerLoudnessEvent
</code>
: Fires when the speaking loudness of a player changes (between normal, whispering and shouting)
<br>
-
<code>
ClientPreAuthEvent
</code>
: A cancellable event that fires whenever a web client attempts to login. Canceling the event will block the login.
<br>
-
<code>
VoiceChatPeerTickEvent
</code>
: This event fires before
<strong>
AND
</strong>
after voice chat peer updates (it has a variable letting you know if it was pre-or post)
<br>
-
<code>
SystemReloadEvent
</code>
: Called whenever the plugin reloads completely or updates state
<br>
-
<code>
ConfigurationPushEvent
</code>
: Fires whenever a new version of the config.yml is loaded through networking, migrations or perhaps redis. It contains the (supposedly) yaml file content as a string.
<br>
<br/>
</p>
<h2 id="getting-a-client">
Getting a Client
</h2>
<p>
A client object resembles the web-connection of a given player and contains api methods (like
<code>
isConnected()
</code>
,
<code>
onConnect
</code>
etc) and is used to specify a player in other API methods.
<br>
You can request a Client by Player-UUID on both bungeecord and spigot, but note that it’ll only be available a few ticks after joining. Example for getting my own connection:
</p><pre><code>Client mindgamesnl = api.getClient(UUID.fromString("f0c8657b-f384-4df6-9d66-e9f36c36ce8a"));
</code></pre>
<p>
We can also hook on connection events, which is as simple as
</p><pre><code>mindgamesnl.onConnect(() -> {
// I opened the web client!
});
</code></pre>
<p>
<br/>
</p>
<h2 id="playing-a-sound">
Playing a sound
</h2>
<p>
Starting a simple sound is as easy as:
</p><pre><code>api.getMediaApi().playMedia(client, "https://example.com/a.mp3");
</code></pre>
<p>
but we can get a lot more creative than that with media options (like setting a Sound ID, playback volume etc), which still is pretty simple, starting a looping sound at half volume with the id “example” would be like:
</p><pre><code>MediaOptions options = new MediaOptions();
options.setLoop(true);
options.setId("example");
options.setVolume(50);
api.getMediaApi().playMedia(client, "https://example.com/a.mp3", options);
</code></pre>
<p>
<br/>
</p>
<h2 id="stopping-sounds">
Stopping sounds
</h2>
<p>
Stopping sounds is even simpler, we can stop all normal sounds through:
</p><pre><code>api.getMediaApi().stopMedia(client);
</code></pre>
<p>
or stop a single sound with the ID “example” with:
</p><pre><code>api.getMediaApi().stopMedia(client, "example");
</code></pre>
<p>
<br/>
</p>
<h2 id="spatial-audio">
Spatial Audio
</h2>
<p>
Explosions are cool, but explosions that spook the living ghost out of someone is even cooler. OpenAudioMc supports spatial audio, and we can simply create it like this:
</p><pre><code>String spatialSoundId = api.getMediaApi().playSpatialSound(client, "https://example.com/a.mp3", x, y, z, 10, true);
</code></pre>
<p>
This will start a 3D spatial sound at a given location for the player with a radius of 10 blocks. You can also just make a simple sound (so one that just does volume instead of 3D orientation by setting the mode to false, which is the last argument).
<br>
the
<code>
playSpatialSound
</code>
method returns a string, which is the spatial-id for that player (and unique to that player). You can remove it again with:
</p><pre><code>api.getMediaApi().stopSpatialSound(client, spatialSoundId);
</code></pre>
<p>
<br/>
</p>
<h2 id="hooking-into-internal-services-and-using-dependency-injection">
Hooking into internal services and using dependency injection
</h2>
<p>
Most of the internal codebase was re-written and refactored during the 6.5.5 update, where we migrated to a custom service manager with support for annotation based dependency injection, service abstraction and to provide pointer safety during reloads.
<br>
The service manager is registered in the main
<code>
OpenAudioMc
</code>
class and is accessible through all platforms. The entire ecosystem consists of two main registration types.
</p>
<ul>
<li>
<strong>
Services
</strong>
are static code implementations that can be injected, requested and manipulated after loading (or being requested, in which case they’ll be loaded if they weren’t already. So calling
<code>
OpenAudioMc.getService(NotLoadedByDefault.class).something()
</code>
will delay the execution of the
<code>
something()
</code>
call, while it’s preparing the
<code>
NotLoadedByDefault
</code>
service and it’s dependencies). Services like this can be registered through:
<br>
<code>
java
serviceManager.loadServices(
FirstService.class,
SecondService.class
);
</code>
<br>
</li>
<li>
<strong>
Mapped Values
</strong>
Some services might have different implementations based on the platform, but are accessed by a shared source (example, having a
<code>
INetworkingService
</code>
interface, being implemented as
<code>
FirstnetworkingImpl
</code>
and
<code>
SecondNetworkingImpl
</code>
) in which case you can register the interface with a value, so you can use dependency injection through the interface, and receive the appropriate implementation class. Example registration:
<br>
<code>
java
OpenAudioMc.getInstance().getServiceManager().registerDependency(TaskService.class, invoker.getTaskProvider());
</code>
<br>
This also means that you can register custom variables (identified by classes or interfaces) and have them injected with a default value or implementation.
<br>
</li>
</ul>
<p>
There are a few ways to receive services, simplest one being the most common one, which is by just requesting the service manually. You can do this at any time through
</p><pre><code>// Get the current MyService instance, or initialize it if it isn't mapped yet
MyService myService = OpenAudioMc.getService(MyService.class);
</code></pre>
<p>
Or you can alternatively use dependency injection using the
<code>
@Inject
</code>
annotation. This supports values and constructors.
</p>
<p>
Field example:
</p><pre><code>public class TestService extends Service {
// inject the main openaudio instance
@Inject
private OpenAudioMc openAudioMc;
public TestService() {
// this module is being loaded
}
@Override
public void onEnable() {
// the injections have been done, so we can safely call this.openAudioMc
}
}
</code></pre>
<p>
or through the constructor, like so:
</p><pre><code>public class TestService extends Service {
@Inject
public TestService(OpenAudioMc openAudioMc, NetworkingService networkingService) {
// both the 'openAudioMc' and 'networkingService' parameters will be injected during init
}
}
</code></pre>
<p>
<strong>
NOTE THAT DEPENDENCY INJECTION ONLY WORKS WHEN YOUR OWN CLASS IS BEING LOADED AS A SERVICE
</strong>
ApiResponse.java:24
</p>
</div>
</div>
</div>
</section>
<footer class="text-gray-400 bg-gray-900 body-font">
<div class="container px-5 py-8 mx-auto flex items-center sm:flex-row flex-col">
<a href="index.html"
class="flex title-font font-medium items-center md:justify-start justify-center text-white">
<img src="assets/logo.png" alt="logo openaudiomc" class="w-10 h-10 text-white bg-indigo-500 rounded-full logo"/>
<span class="ml-3 text-xl">OpenAudioMc</span>
</a>
<p class="text-sm text-gray-400 sm:ml-4 sm:pl-4 sm:border-l-2 sm:border-gray-800 sm:py-2 sm:mt-0 mt-4">© 2022
OpenAudioMc —
<a href="https://twitter.com/Mindgamesnl" class="text-gray-500 ml-1" target="_blank"
rel="noopener noreferrer">@Mindgamesnl</a>
</p>
</div>
</footer>
</body>
</html>