@@ -390,7 +390,7 @@ import org.bson.Document;
390390// Get direct access to the shared database connection
391391MongoDatabase database = api. getDatabase();
392392
393- // Create your own collection (bijvoorbeeld voor cosmetics)
393+ // Create your own collection (for example for cosmetics)
394394MongoCollection<Document > cosmetics = database. getCollection(" cosmetics" );
395395
396396// Perform operations on your custom collection
@@ -415,16 +415,290 @@ Document playerCosmeticData = new Document("_id", playerUUID.toString())
415415playerCosmetics. 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
0 commit comments