[Plugin] Shoebill 1.1 - SA-MP Java Development Kit

Thanks for your response. I've looked over some bits of code and I the more I look over it the more I seem to understand. As already stated, I'm just not sure if putting much effort in a big scale project with shoebill is future-proof. I really don't like relying on other people to update stuff everytime SA-MP is updated, even though that is only once a year.
Reply

@Manyula: The code was very systematically built, most parts of the API will work the same way as the other. If you know how to create a Vehicle, you will know how to create a SampObject or a Checkpoint.

Shoebill was already created in 2011, I don't think you will have to worry about updates.
Reply

@mk124: could you please give us more information about threads? I've seen some posts where you've written that using multiple threads and not running them on SAMP one may cause a server to crash. I would like to know some conditions in which it will definitely happen.
The reason why am I asking is that I'm using hibernate to store data in database and I would like to load and save data independently of the main code execution.

I suppose, the server can crash in cases where there are some SAMP functions are called (like get player location, skin, color etc) in java threads, am I right? Or it depends on chance?

Back to my question, the data loading and saving doesn't have anything with SAMP functions - so may I be sure that server won't crash after or during the thread execution?
Reply

@valych: You can use multiple threads if you want to save and load data from a database server, as long as the database server supports multithreading. As soon as you try to use any kind of internal function of the SA:MP server, you will have to let that run on the main SA:MP thread, because otherwise, the server will crash.

Here is a example:

PHP код:
        Thread fetchThread = new Thread(() -> {
            
//Fetching my data:
            
Map<StringStringlocationData = new HashMap<>(); //example
            
try {
                
Thread.sleep(5000); //simulate workload
                
locationData.put("Player1""Germany"); //example data
                
locationData.put("Player2""Italy"); //example data
                
locationData.put("Player3""Poland"); //example data
            
} catch (InterruptedException e) {
                
e.printStackTrace();
            }

            
Shoebill.get().runOnSampThread(() -> {
                
//Do whatever you want to do with the data if it has something to do with SA:MP
                
Player.getHumans().stream().filter(player -> locationData.containsKey(player.getName())).forEach(player -> {
                    
player.sendMessage(Color.GREEN"you are from " locationData.get(player.getName()));
                });
            });
        });
        
fetchThread.start(); //Start thread 
Reply

Hi again. I have a question: I want to test my code. Everything was fine, I created "mock" implementations of Player, Vehicle, etc, to use in tests. Now, the problem is that everytime I get a NullPointerException when I use commons dialogs. Why? Because dialogs uses SampObjectManager.get() to be created.

Seeing the impl, it just asks to Shoebill.get().getSampObjectManager. Now my question: How can I inject my "own" instance of shoebill? I need to create a "mock" instance to test everything, like this:

PHP код:
class MockShoebill implements Shoebill {
    ...     
 
    @
Override
    
public SampObjectManager getObjectManager() {
        return new 
SampObjectManager() {
            
createDialogId(..) {
                return 
MockDialogId(..);
            }
        }
    }
    ...

to decouple shoebill from runtime dependencies. My first idea was to use a proxy, then intercept shoebill "get". However, I dont know if its possible to intercept interface default methods.
Reply

This looks great. I might try my luck with this.
Reply

Quote:
Originally Posted by lucesita
Посмотреть сообщение
Hi again. I have a question: I want to test my code. Everything was fine, I created "mock" implementations of Player, Vehicle, etc, to use in tests. Now, the problem is that everytime I get a NullPointerException when I use commons dialogs. Why? Because dialogs uses SampObjectManager.get() to be created.

Seeing the impl, it just asks to Shoebill.get().getSampObjectManager. Now my question: How can I inject my "own" instance of shoebill? I need to create a "mock" instance to test everything, like this:

PHP код:
class MockShoebill implements Shoebill {
    ...     
 
    @
Override
    
public SampObjectManager getObjectManager() {
        return new 
SampObjectManager() {
            
createDialogId(..) {
                return 
MockDialogId(..);
            }
        }
    }
    ...

to decouple shoebill from runtime dependencies. My first idea was to use a proxy, then intercept shoebill "get". However, I dont know if its possible to intercept interface default methods.
Why did you create your own implementations of Shoebill objects?
Reply

Hello again
I have a question about the timers again.
I want to create an AntiSpawnKill by setting the health of the player to 10000.0 and after 40 seconds it will be reseted to 100.0.
I am planning on creating a normal timer and keeping the Timer Id for each player since when the player dies for whatever reason before the "callback" is called or disconnects the timer will be stopped.
I have more doubts about the timers because I saw timers stop working when you disconnect from the server but I have no idea how it works.
More problems I could have:
(When I want to send everyone a message or something else after the player disconnects, i.g. 1 minute later guess the timer won't work)
Reply

Quote:
Originally Posted by dusk
Посмотреть сообщение
Why did you create your own implementations of Shoebill objects?
For testing. There are a maven step called test, which will execute all unit tests that you have under test/java folder. You cant call samp functions here, because you need to link samp server with the java virtual machine for that.
I want to create shoebill implementations that "simulates" a running server
Reply

@Su37Erich: Here is an example on how to create a hp reset after 40 seconds. It is important that you only have one instance of a timer for each player active. Because when the player dies twice in these 40 seconds, the timer will be started multiple times. It is also important that you check that the player is still connected after these 40 seconds. In this example, I will use the PlayerLifecycleHolder:

PHP код:
class UserData extends PlayerLifecycleObject {
    private static final 
int HEALTH_RESET_INTERVAL 40000//40 secs
    
private Timer healthReset;
    public 
UserData(EventManager eventManagerPlayer player) {
        
super(eventManagerplayer);
    }
    @
Override
    
protected void onInit() {
        
eventManagerNode.registerHandler(PlayerDeathEvent.class, HandlerPriority.NORMAL,
                
Attentions.create().object(player), deathEvent -> {
                    if(
deathEvent.getPlayer() != player) return; //The event will maybe fire when the killer is the player, and we don't want that. We only want this event if the died player is the player.                   
                    
if (healthReset != null && healthReset.isRunning())
                        
healthReset.stop(); //Stop previous timer if it was running
                    
healthReset Timer.create(HEALTH_RESET_INTERVAL1-> {
                        if (
player.isOnline()) {
                            
player.setHealth(100f);
                        }
                        
healthReset null;
                    });
                    
healthReset.start();
                    
EventManagerNode spawnNode eventManagerNode.createChildNode();
                    
spawnNode.registerHandler(PlayerSpawnEvent.class, HandlerPriority.NORMAL,
                            
Attentions.create().object(player), spawnEvent -> {
                                
player.setHealth(100000f);
                                
spawnNode.destroy();
                            });
                });
    }
    @
Override
    
protected void onDestroy() {
    }

Let me explain what this code is doing:
When the UserData class gets initialized by the PlayerLifecycleHolder, a new event handler for the PlayerDeath event is registered, but only for the specific player (look at the Attentions.create().object(player) part. The "player" field gets inherited from the PlayerLifecycleObject superclass). When the event gets fired, it will check if a healthReset timer is already running, and if it is, it will be stopped. After that, a new timer will be created that will fire once after 40 seconds. When the timer gets fired, it will check if the player is still connected, and if it is, it will set the health of the player to 100 HP. After that, the timer will set itself to null, just to mark it as "not in use". When the timer has been setted up in the DeathEvent, it will be started. But we also want that the player's health will be set to 10000 HP when he spawns again, so we register a new event manager node with a new handler for the PlayerSpawnEvent but only for the specific player (see Attentions.create()....). In the event, the health of the player will be set to 10000 HP and after that, the handler will destroy itself.
Reply

Thank you!
It helped me a lot!
Byt the way it would be really nice if you could add something like:
DialogCloseEvent.DialogCloseType PLAYERDISCONNECT
Guess I need to do a workaround if want to detect the dialog id when a player disconnects?
Reply

@Su37Erich: I added this to the Issue Tracker: http://youtrack.gtaun.net/issue/SA-5 and will implement it in Shoebill 2.0.
Reply

Thank you
I would like to know if there is some problem with the streamer-wrapper, every time I want to create an object I get the follwing exception:
Код:
[2016-05-28 14:23:31][ERROR][err] kotlin.KotlinNullPointerException
[2016-05-28 14:23:32][ERROR][err] 	at net.gtaun.shoebill.streamer.Functions.createDynamicObject(Functions.kt:185)
[2016-05-28 14:23:32][ERROR][err] 	at net.gtaun.shoebill.streamer.data.DynamicObject$Companion.create(DynamicObject.kt:145)
[2016-05-28 14:23:32][ERROR][err] 	at net.gtaun.shoebill.streamer.data.DynamicObject$Companion.create$default(DynamicObject.kt:141)
[2016-05-28 14:23:32][ERROR][err] 	at net.gtaun.shoebill.streamer.data.DynamicObject.create(DynamicObject.kt)
[2016-05-28 14:23:32][ERROR][err] 	at dev.acesamp.Maps.Map.loadObjects(Map.java:48)
[2016-05-28 14:23:32][ERROR][err] 	at dev.acesamp.Maps.Map.<init>(Map.java:20)
[2016-05-28 14:23:32][ERROR][err] 	at dev.acesamp.Maps$LoadedMap.<init>(Maps.java:33)
[2016-05-28 14:23:32][ERROR][err] 	at dev.acesamp.Maps.Maps.<init>(Maps.java:78)
[2016-05-28 14:23:32][ERROR][err] 	at dev.acesamp.MyGamemode.onEnable(MyGamemode.java:38)
[2016-05-28 14:23:32][ERROR][err] 	at net.gtaun.shoebill.resource.Resource.enable(Resource.java:91)
[2016-05-28 14:23:32][ERROR][err] 	at net.gtaun.shoebill.resource.ResourceManagerImpl.loadGamemode(ResourceManagerImpl.java:179)
[2016-05-28 14:23:32][ERROR][err] 	at net.gtaun.shoebill.resource.ResourceManagerImpl.loadAllResource(ResourceManagerImpl.java:62)
[2016-05-28 14:23:32][ERROR][err] 	at net.gtaun.shoebill.ShoebillImpl.loadPluginsAndGamemode(ShoebillImpl.java:289)
[2016-05-28 14:23:32][ERROR][err] 	at net.gtaun.shoebill.ShoebillImpl.access$100(ShoebillImpl.java:47)
[2016-05-28 14:23:32][ERROR][err] 	at net.gtaun.shoebill.ShoebillImpl$1.onAmxLoad(ShoebillImpl.java:233)
[2016-05-28 14:23:32][ERROR][err] 	at net.gtaun.shoebill.samp.SampCallbackManagerImpl$1.lambda$null$80(SampCallbackManagerImpl.java:60)
[2016-05-28 14:23:32][ERROR][err] 	at net.gtaun.shoebill.util.TryUtils.tryTo(TryUtils.java:21)
[2016-05-28 14:23:32][ERROR][err] 	at net.gtaun.shoebill.util.TryUtils.tryTo(TryUtils.java:14)
[2016-05-28 14:23:32][ERROR][err] 	at net.gtaun.shoebill.samp.SampCallbackManagerImpl$1.lambda$onAmxLoad$81(SampCallbackManagerImpl.java:60)
[2016-05-28 14:23:32][ERROR][err] 	at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
[2016-05-28 14:23:32][ERROR][err] 	at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
[2016-05-28 14:23:32][ERROR][err] 	at java.util.concurrent.ConcurrentLinkedQueue$CLQSpliterator.forEachRemaining(ConcurrentLinkedQueue.java:851)
[2016-05-28 14:23:32][ERROR][err] 	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
[2016-05-28 14:23:32][ERROR][err] 	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
[2016-05-28 14:23:32][ERROR][err] 	at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
[2016-05-28 14:23:33][ERROR][err] 	at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
[2016-05-28 14:23:33][ERROR][err] 	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
[2016-05-28 14:23:33][ERROR][err] 	at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
[2016-05-28 14:23:33][ERROR][err] 	at net.gtaun.shoebill.samp.SampCallbackManagerImpl$1.onAmxLoad(SampCallbackManagerImpl.java:60)
My code
Код:
streamerObjects.add(DynamicObject.create(model, new Location(x, y, z, interior, virtualWorld), new Vector3D(rx, ry, rz), streamDistance, distance));
The data:

model: 18449, x: 2953.3, y: -781.94, z:11.45, rx: 0, ry: 0, rz:357.43, distance: 700, streamDistance: 400, interior: -1, virtualworld: -1

I tested it with the 2.7.9 and 2.8.1 streamer plugin version
Reply

@Su37Erich: Looks like the native function for creating a dynamic object was not found. Are you sure that you load the streamer plugin before Shoebill? Make sure that the streamer entry is positioned before the Shoebill entry in the plugins section in the server.cfg.
Reply

Yes I am sure:

echo Executing Server Config...
lanmode 0
rcon_password dontchangeme
maxplayers 100
port 7777
hostname Shoebill SA-MP 0.3 Server
gamemode0 bare 1
filterscripts basefs maps
plugins streamer Shoebill
announce 1
query 1
weburl sa-mp.com
onfoot_rate 40
incar_rate 40
weapon_rate 40
stream_distance 300.0
stream_rate 1000
maxnpc 10
logtimeformat [%H:%M:%S]


EDIT: I just unlocked the file, but now the map doesn't appear and there are not logs
Reply

@Su37Erich: make sure that you are using the latest version of streamer plugin, then make sure that you compile you java code with streamer wrapper 1.1, and finally - make sure that you add dependency to your resources,yml streamer wrapper 1.1
Reply

Yes, I am using the latest version and the streamer wrapper 1.1

Код:
<dependency>
            <groupId>net.gtaun.shoebill</groupId>
            <artifactId>streamer-wrapper</artifactId>
            <version>1.1-SNAPSHOT</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
I get these messages in the console:
Код:
*** Streamer Plugin v2.8.1 by Incognito loaded ***

   Loaded.
  Loading plugin: Shoebill
   > Shoebill 1.3 NativePlugin for SA-MP 0.3.7 by MK124, JoJLlmAn & 123marvin123
  > Java VM has been created.
   > Shoebill has been initialized.
   Loaded.
  Loaded 2 plugins.
Resolved artifact net.gtaun.shoebill:streamer-wrapper:jar:1.1-20160522.140640-6 from local
With the older version everything was working fine except I had exceptions while deleting objects
I don't know what to add and where, to the file resources.yml I think I don't have to
Reply

It is possible, that you haven't updated the streamer-wrapper. The version of the plugin in 1.1-snapshot wasn't changed in initial commit. Try to update streamer-wrapper once more (in case you have offlineMode turned on).
Reply

Its a very good job. But Shoebill realy needs a wiki, forum, something idk. You need something more than this:http://shoebill.github.io
I do not want to just read methods of a class and their parameters. You should have a wiki and add description for every class and its members and write some tutorials. I dont need a web site to read methods names! I can do it with opening .java files and its realy better. Why you dont create a wiki?
Reply

Docummentation takes time. A lot of it! Marvin also said he is rewritting shoebill in Kotlin language so it will change.
Reply


Forum Jump:


Users browsing this thread: 2 Guest(s)