Skip to content

Commit a3d82f9

Browse files
committed
feat: Add custom database support per plugin
1 parent 1e3abab commit a3d82f9

File tree

6 files changed

+338
-22
lines changed

6 files changed

+338
-22
lines changed

.github/STATUS_COMPLETE.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
# ✅ Workflows Status - VOLLEDIG WERKEND
22

3-
## Laatste Status Update
3+
## 🎉 LAATSTE UPDATE: Alle Fixes zijn GEPUSHT naar GitHub!
44

5-
### ✅ SUCCESVOL
5+
**Commit:** `bba6dea` - "fix: add workflow permissions and jetty servlet dependencies"
6+
7+
### ✅ SUCCESVOL GEPUSHT
68
Alle workflows draaien nu correct met Java 21 en de juiste permissions!
79

8-
### Wat is er gefixed?
10+
**De volgende push/commit zal nu ZONDER errors draaien!**
11+
12+
### Wat is er gefixed EN GEPUSHT?
913

1014
#### 1. **Java 21 Update**
1115
Alle workflows gebruiken nu Java 21:

API_DOCUMENTATION.md

Lines changed: 284 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ import org.bson.Document;
390390
// Get direct access to the shared database connection
391391
MongoDatabase database = api.getDatabase();
392392

393-
// Create your own collection (bijvoorbeeld voor cosmetics)
393+
// Create your own collection (for example for cosmetics)
394394
MongoCollection<Document> cosmetics = database.getCollection("cosmetics");
395395

396396
// Perform operations on your custom collection
@@ -415,16 +415,290 @@ Document playerCosmeticData = new Document("_id", playerUUID.toString())
415415
playerCosmetics.insertOne(playerCosmeticData);
416416
```
417417

418-
**Voordelen van deze aanpak:**
419-
-**Geen eigen database connectie** nodig in je plugin
420-
-**Gebruikt de gedeelde connection pool** (max 100 connections)
421-
-**Automatische reconnection** en error handling
422-
-**Minder resource usage** - alle plugins delen dezelfde pool
423-
-**Eenvoudige setup** - één regel code: `api.getDatabase()`
424-
-**Volledige MongoDB API** - alle operations beschikbaar
418+
**Benefits of this approach:**
419+
-**No separate database connection** needed in your plugin
420+
-**Uses the shared connection pool** (max 100 connections)
421+
-**Automatic reconnection** and error handling
422+
-**Less resource usage** - all plugins share the same pool
423+
-**Simple setup** - one line of code: `api.getDatabase()`
424+
-**Full MongoDB API** - all operations available
425425

426-
**Complete voorbeeld voor een Cosmetics Plugin:**
427-
Zie `COSMETICS_PLUGIN_EXAMPLE.java` in de repository voor een volledig werkend voorbeeld!
426+
### Custom Databases per Plugin
427+
428+
**New in v1.0!** Each plugin can now have its **own MongoDB database** for **complete isolation**:
429+
430+
```java
431+
import com.mongodb.client.MongoDatabase;
432+
import com.mongodb.client.MongoCollection;
433+
import org.bson.Document;
434+
435+
// Each plugin gets its own database
436+
MongoDatabase cosmeticsDB = api.getDatabase("cosmetics_plugin");
437+
MongoDatabase guildsDB = api.getDatabase("guilds_plugin");
438+
MongoDatabase punishmentsDB = api.getDatabase("punishments_plugin");
439+
440+
// Work with your own database - 100% isolated!
441+
MongoCollection<Document> items = cosmeticsDB.getCollection("items");
442+
MongoCollection<Document> purchases = cosmeticsDB.getCollection("purchases");
443+
444+
// No conflicts with other plugins possible!
445+
items.insertOne(new Document("name", "Crown").append("price", 5000));
446+
```
447+
448+
**When to use a separate database?**
449+
-**Large plugins** with lots of data (1M+ documents)
450+
-**Complete isolation** from other plugins
451+
-**Own backup schema** per plugin
452+
-**Different replication settings**
453+
-**Easier data management** and migrations
454+
-**Separate monitoring** per plugin
455+
456+
**When to use the same database with separate collections?**
457+
-**Small to medium-sized plugins**
458+
-**Cross-plugin queries** needed
459+
-**Simpler setup**
460+
461+
**Complete example: Cosmetics Plugin with its own database**
462+
463+
```java
464+
import com.astroid.stijnjakobs.networkdataapi.core.api.APIRegistry;
465+
import com.astroid.stijnjakobs.networkdataapi.core.api.NetworkDataAPIProvider;
466+
import com.mongodb.client.MongoCollection;
467+
import com.mongodb.client.MongoDatabase;
468+
import com.mongodb.client.model.Filters;
469+
import com.mongodb.client.model.UpdateOptions;
470+
import com.mongodb.client.model.Updates;
471+
import org.bson.Document;
472+
import org.bukkit.plugin.java.JavaPlugin;
473+
474+
import java.util.*;
475+
import java.util.concurrent.CompletableFuture;
476+
import java.util.stream.Collectors;
477+
478+
public class CosmeticsPlugin extends JavaPlugin {
479+
480+
private NetworkDataAPIProvider api;
481+
private MongoDatabase database;
482+
private MongoCollection<Document> itemsCollection;
483+
private MongoCollection<Document> playerCosmeticsCollection;
484+
485+
@Override
486+
public void onEnable() {
487+
// Hook into NetworkDataAPI
488+
api = APIRegistry.getAPI();
489+
490+
// Get your own dedicated database
491+
database = api.getDatabase("cosmetics_plugin");
492+
493+
// Initialize collections
494+
itemsCollection = database.getCollection("items");
495+
playerCosmeticsCollection = database.getCollection("player_cosmetics");
496+
497+
// Create indexes for better performance
498+
createIndexes();
499+
500+
// Load cosmetic items from database
501+
loadCosmeticItems();
502+
503+
getLogger().info("Cosmetics Plugin using dedicated database: cosmetics_plugin");
504+
}
505+
506+
private void createIndexes() {
507+
// Index on item type for faster queries
508+
itemsCollection.createIndex(new Document("type", 1));
509+
510+
// Index on rarity for faster filtering
511+
itemsCollection.createIndex(new Document("rarity", 1));
512+
513+
// Compound index for player queries
514+
playerCosmeticsCollection.createIndex(
515+
new Document("uuid", 1).append("equipped", 1)
516+
);
517+
}
518+
519+
private void loadCosmeticItems() {
520+
// Count items
521+
long itemCount = itemsCollection.countDocuments();
522+
523+
if (itemCount == 0) {
524+
getLogger().info("No cosmetic items found. Creating defaults...");
525+
createDefaultItems();
526+
} else {
527+
getLogger().info("Loaded " + itemCount + " cosmetic items");
528+
}
529+
}
530+
531+
private void createDefaultItems() {
532+
// Create default cosmetic items
533+
List<Document> defaultItems = Arrays.asList(
534+
new Document("_id", "party_hat")
535+
.append("name", "Party Hat")
536+
.append("type", "HAT")
537+
.append("rarity", "RARE")
538+
.append("price", 1000),
539+
540+
new Document("_id", "crown")
541+
.append("name", "Royal Crown")
542+
.append("type", "HAT")
543+
.append("rarity", "LEGENDARY")
544+
.append("price", 5000),
545+
546+
new Document("_id", "hearts_trail")
547+
.append("name", "Hearts Trail")
548+
.append("type", "TRAIL")
549+
.append("rarity", "UNCOMMON")
550+
.append("price", 500)
551+
);
552+
553+
itemsCollection.insertMany(defaultItems);
554+
getLogger().info("Created " + defaultItems.size() + " default items");
555+
}
556+
557+
// API Methods for your plugin
558+
559+
public CompletableFuture<List<Document>> getPlayerCosmetics(UUID playerUUID) {
560+
return CompletableFuture.supplyAsync(() -> {
561+
Document playerData = playerCosmeticsCollection.find(
562+
Filters.eq("_id", playerUUID.toString())
563+
).first();
564+
565+
if (playerData == null) {
566+
return Collections.emptyList();
567+
}
568+
569+
return playerData.getList("owned", String.class).stream()
570+
.map(itemId -> itemsCollection.find(Filters.eq("_id", itemId)).first())
571+
.filter(Objects::nonNull)
572+
.collect(Collectors.toList());
573+
});
574+
}
575+
576+
public CompletableFuture<Boolean> purchaseCosmetic(UUID playerUUID, String itemId) {
577+
return CompletableFuture.supplyAsync(() -> {
578+
// Get item
579+
Document item = itemsCollection.find(Filters.eq("_id", itemId)).first();
580+
if (item == null) return false;
581+
582+
int price = item.getInteger("price", 0);
583+
584+
// Check if player can afford it (using NetworkDataAPI player data)
585+
Document playerData = api.getPlayerDataService().getPlayerData(playerUUID);
586+
int coins = playerData.getInteger("coins", 0);
587+
588+
if (coins < price) return false;
589+
590+
// Deduct coins
591+
api.getPlayerDataService().incrementField(playerUUID, "coins", -price);
592+
593+
// Add cosmetic to player
594+
playerCosmeticsCollection.updateOne(
595+
Filters.eq("_id", playerUUID.toString()),
596+
Updates.addToSet("owned", itemId),
597+
new UpdateOptions().upsert(true)
598+
);
599+
600+
return true;
601+
});
602+
}
603+
604+
public CompletableFuture<Void> equipCosmetic(UUID playerUUID, String itemId) {
605+
return CompletableFuture.runAsync(() -> {
606+
// Get item type
607+
Document item = itemsCollection.find(Filters.eq("_id", itemId)).first();
608+
if (item == null) return;
609+
610+
String type = item.getString("type");
611+
612+
// Equip the cosmetic
613+
playerCosmeticsCollection.updateOne(
614+
Filters.eq("_id", playerUUID.toString()),
615+
Updates.set("equipped." + type.toLowerCase(), itemId),
616+
new UpdateOptions().upsert(true)
617+
);
618+
});
619+
}
620+
}
621+
```
622+
623+
**Benefits of own database per plugin:**
624+
-**Complete isolation** - no conflicts possible
625+
-**Own backup strategy** per plugin
626+
-**Better organization** for large datasets
627+
-**Separate monitoring** and performance tuning
628+
-**Uses the same connection pool** - efficient!
629+
-**No extra configuration** - works out-of-the-box
630+
631+
**Database Management Best Practices:**
632+
633+
```java
634+
// ❌ WRONG: Creating your own MongoClient
635+
MongoClient myOwnClient = MongoClients.create("mongodb://localhost:27017");
636+
// This wastes resources and connections!
637+
638+
// ✅ CORRECT: Use NetworkDataAPI's connection
639+
MongoDatabase myDB = api.getDatabase("my_plugin");
640+
// Uses the shared, configured connection pool!
641+
```
642+
643+
**Example: Guild Plugin with its own database**
644+
645+
```java
646+
import com.astroid.stijnjakobs.networkdataapi.core.api.APIRegistry;
647+
import com.astroid.stijnjakobs.networkdataapi.core.api.NetworkDataAPIProvider;
648+
import com.mongodb.client.MongoCollection;
649+
import com.mongodb.client.MongoDatabase;
650+
import org.bson.Document;
651+
import org.bukkit.plugin.java.JavaPlugin;
652+
653+
import java.util.UUID;
654+
655+
public class GuildsPlugin extends JavaPlugin {
656+
657+
private MongoDatabase guildsDatabase;
658+
private MongoCollection<Document> guildsCollection;
659+
private MongoCollection<Document> guildMembersCollection;
660+
661+
@Override
662+
public void onEnable() {
663+
NetworkDataAPIProvider api = APIRegistry.getAPI();
664+
665+
// Dedicated database for guilds
666+
guildsDatabase = api.getDatabase("guilds_plugin");
667+
668+
guildsCollection = guildsDatabase.getCollection("guilds");
669+
guildMembersCollection = guildsDatabase.getCollection("guild_members");
670+
671+
// Create indexes
672+
guildsCollection.createIndex(new Document("name", 1));
673+
guildsCollection.createIndex(new Document("level", -1));
674+
guildMembersCollection.createIndex(new Document("guildId", 1));
675+
}
676+
677+
public void createGuild(String guildName, UUID ownerUUID) {
678+
Document guild = new Document()
679+
.append("name", guildName)
680+
.append("owner", ownerUUID.toString())
681+
.append("level", 1)
682+
.append("members", 1)
683+
.append("createdAt", System.currentTimeMillis())
684+
.append("bankBalance", 0);
685+
686+
guildsCollection.insertOne(guild);
687+
688+
// Add owner as member
689+
Document member = new Document()
690+
.append("uuid", ownerUUID.toString())
691+
.append("guildId", guild.getObjectId("_id"))
692+
.append("rank", "OWNER")
693+
.append("joinedAt", System.currentTimeMillis());
694+
695+
guildMembersCollection.insertOne(member);
696+
}
697+
}
698+
```
699+
700+
**Complete example for a Cosmetics Plugin:**
701+
See `COSMETICS_PLUGIN_EXAMPLE.java` in the repository for a fully working example!
428702

429703
### Working with Nested Documents
430704

networkdataapi-bungee/pom.xml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,9 @@
7878
</includes>
7979
</artifactSet>
8080
<relocations>
81-
<relocation>
82-
<pattern>com.mongodb</pattern>
83-
<shadedPattern>com.astroid.stijnjakobs.networkdataapi.libs.mongodb</shadedPattern>
84-
</relocation>
81+
<!-- MongoDB is NOT relocated to maintain API compatibility -->
82+
<!-- External plugins can use standard com.mongodb.* imports -->
83+
8584
<relocation>
8685
<pattern>com.github.benmanes.caffeine</pattern>
8786
<shadedPattern>com.astroid.stijnjakobs.networkdataapi.libs.caffeine</shadedPattern>

networkdataapi-core/src/main/java/com/astroid/stijnjakobs/networkdataapi/core/api/APIRegistry.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ public com.mongodb.client.MongoDatabase getDatabase() {
9393
return coreManager.getDatabaseManager().getDatabase();
9494
}
9595

96+
@Override
97+
public com.mongodb.client.MongoDatabase getDatabase(String databaseName) {
98+
return coreManager.getDatabaseManager().getClient().getDatabase(databaseName);
99+
}
100+
96101
@Override
97102
public String getVersion() {
98103
return "1.0-SNAPSHOT";

0 commit comments

Comments
 (0)