I have a plugin that works with different commands throughout different classes. However, in this case when I have an argument length of 2, the command just doesn't seem to register.
public class FinalFrontierAdminCmds implements CommandExecutor{
FinalFrontier get;
#Override
public boolean onCommand(CommandSender sender, Command cmd, String cmdLabel,
String[] args) {
Player p = (Player) sender;
if (cmd.getName().equalsIgnoreCase("ff")){
// Check if only /ff is typed
if (args.length == 0){
p.sendMessage("This will display help menu");
return true;
}
if (args.length == 2){
if (args[0].equalsIgnoreCase("create")){
if(args[1] != null){
get.getConfig().set("Maps." + args[1] + ".world", p.getLocation().getWorld());
get.getConfig().set("Maps." + args[1] + ".x", p.getLocation().getBlockX());
get.getConfig().set("Maps." + args[1] + ".y", p.getLocation().getBlockY());
get.getConfig().set("Maps." + args[1] + ".z", p.getLocation().getBlockZ());
get.getConfig().set("Maps." + args[1] + ".isSet", false);
p.sendMessage(get.ffMsg + "You have succesfully created map " + ChatColor.GREEN + args[1] + ChatColor.YELLOW + "!");
return true;
}
}
}
return true;
}
return false;
}
}
Main class with onEnable and onDisable:
public class FinalFrontier extends JavaPlugin{
String ffMsg = ChatColor.GREEN + "[" + ChatColor.YELLOW + "Final Frontier" + ChatColor.GREEN + "]" + ChatColor.YELLOW + ": ";
public void onEnable(){
getConfig().addDefault("Maps.", "");
this.getCommand("ff").setExecutor(new FinalFrontierAdminCmds());
saveConfig();
}
public void onDisable(){
saveConfig();
}
}
This is the first time this happens to me. I usually do it like this, unless something has changed? Thanks
The line getConfig().addDefault("Maps.", ""); throws an IllegalArgumentException error because you are trying to set an empty path (the empty path being the non-existent string after the "."). If you remove the period it will create that section correctly.
I didn't see any code in your command executor class that initializes the get variable. I would add the constructor (if you haven't already done so),
public FinalFrontierAdminCmds(FinalFrontier plugin) {
this.get = plugin;
}
and update the instantiation of the object in your onEnable() method to reflect this change (this.getCommand("ff").setExecutor(new FinalFrontierAdminCmds(this))), otherwise your command executor class will throw an NPE when trying to add the values.
Last but not least I would also save the world's name (p.getLocation().getWorld().getName()) and not pass the world object itself to the set method, otherwise the config file will look something like this:
Maps:
example:
world: !!org.bukkit.craftbukkit.v1_8_R3.CraftWorld
PVP: true
ambientSpawnLimit: 15
animalSpawnLimit: 15
autoSave: true
difficulty: PEACEFUL
environment: NORMAL
fullTime: 1100
keepSpawnInMemory: true
monsterSpawnLimit: 70
thunderDuration: 32301
thundering: false
time: 1100
waterAnimalSpawnLimit: 5
weatherDuration: 56712
//more values down here
With these fixes your code should work as expected.
Related
I don't know what I should put into player.getAdvancementProgress(Here).
if (player.getAdvancementProgress().isDone()) {
}
Maybe someone knows something?
You should use an Advancement object, specially the advancement that you are looking for informations.
You can get it with Bukkit.getAdvancement(NamespacedKey.fromString("advancement/name")) where advancement/name can be nether/all_potions for example. You can get all here (column: "Resource location). If you are getting it from command, I suggest you to add tab complete.
Example of TAB that show only not-done success :
#Override
public List<String> onTabComplete(CommandSender sender, Command cmd, String label, String[] arg) {
List<String> list = new ArrayList<>();
if(!(sender instanceof Player))
return list;
Player p = (Player) sender;
String prefix = arg[arg.length - 1].toLowerCase(Locale.ROOT); // the begin of the searched advancement
Bukkit.advancementIterator().forEachRemaining((a) -> {
AdvancementProgress ap = p.getAdvancementProgress(a);
if((prefix.isEmpty() || a.getKey().getKey().toLowerCase().startsWith(prefix)) && !ap.isDone() && !a.getKey().getKey().startsWith("recipes"))
list.add(a.getKey().getKey());
});
return list;
}
Then, in the command you can do like that:
#Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] arg) {
if(!(sender instanceof Player)) // not allowed for no-player
return false;
Player p = (Player) sender;
// firstly: try to get advancement
Advancement a = Bukkit.getAdvancement(NamespacedKey.fromString(arg[0]));
if(a == null)
a = Bukkit.getAdvancement(NamespacedKey.minecraft(arg[0]));
if(a == null) // can't find it
p.sendMessage(ChatColor.RED + "Failed to find success " + arg[0]);
else { // founded :
AdvancementProgress ap = p.getAdvancementProgress(a);
p.sendMessage(ChatColor.GREEN + "Achivement " + a.getKey().getKey() + " stay: " + ChatColor.YELLOW + String.join(", ", ap.getRemainingCriteria().stream().map(this::getCleaned).collect(Collectors.toList())));
}
return false;
}
private String getCleaned(String s) { // this method is only to make content easier to read
String[] args = s.split("/");
return args[args.length - 1].replace(".png", "").replace(".jpg", "").replace("minecraft:", "").replace("_", " ");
}
Else, if you want to get all advancements, you should use Bukkit.advancementIterator().
I am trying to check if the item the player used is the custom item I added but for some reason it is not detecting the item. Here is the code.
#EventHandler
public void damage(EntityDamageByEntityEvent event){
if(event.getEntity() instanceof Player && event.getDamager() instanceof Player){
if(((Player) event.getDamager()).getInventory().getItemInMainHand() == CustomItems.potator()){
System.out.println("potato");
}
else{
//When i looked # console this logged the same exact thing
System.out.println("Damager Main Item = " + ((Player) event.getDamager()).getInventory().getItemInMainHand());
System.out.println("Potator Item = " + CustomItems.potator());
}
}
}
Custom Item Class:
public static ItemStack potator(){
ArrayList<String> lore = new ArrayList<String>();
lore.add(ChatColor.GOLD + "Turns a random slot in the hotbar to a potato.");
lore.add(ChatColor.GRAY + "1/100% chance from dropping from a potato.");
ItemStack item = new ItemStack(Material.STONE_HOE);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.GOLD + "The Potator");
meta.setLore(lore);
item.setItemMeta(meta);
return item;
}
if(((Player) event.getDamager()).getInventory().getItemInMainHand() == CustomItems.potator())
In this line, by using ==, you're checking if the left and right-hand references are the same. You want to do content comparison, e.g. using .equals(). There's a good explanation of the differences on GeeksForGeeks.
Spigot provides a stack size independent method isSimilar() which would be better than .equals().
However, this would likely make your code execute for any stone hoe so you'll need to do your own checks. I would do something like this:
public boolean isPotator(ItemStack i){
if(i == null || !i.hasItemMeta())
return false;
ItemMeta meta = i.getItemMeta();
if(!meta.hasDisplayName() || !meta.hasLore())
return false;
return meta.getDisplayName().equals(ChatColor.GOLD + "The Potator");
}
And then perform your check like so:
#EventHandler
public void damage(EntityDamageByEntityEvent event)
{
if(!(event.getEntity() instanceof Player && event.getDamager() instanceof Player))
return;
Player damager = (Player) event.getDamager();
if(!isPotator(damager.getInventory().getItemInMainHand()))
return;
System.out.println("potato");
}
Since you have a CustomItem class, you can better this code by making a final string for the item name, which you can then use in both the ItemStack creation and the isPotator check.
I'm having a weird issue with creating paths using Qt on Linux. I've written a standalone test program that creates paths and tests for their existence. This works as expected and creates the directory.
/* make path */
QString p("/usr2/archive/S1234ABC/5/6");
QDir d;
if (d.mkpath(p)) qDebug() << "mkpath() returned true";
else qDebug() << "mkpath() returned false";
QDir d2;
if (d2.exists(p)) qDebug() << "exists() returned true";
else qDebug() << "exists() returned false";
I made that test example into a more robust function, in another project. But it isn't working... mkpath() and exists() return true, but the paths don't exist on the hard disk.
bool nidb::MakePath(QString p, QString &msg) {
if ((p == "") || (p == ".") || (p == "..") || (p == "/") || (p.contains("//")) || (p == "/root") || (p == "/home")) {
msg = "Path is not valid [" + p + "]";
return false;
}
WriteLog("MakePath() called with path ["+p+"]");
QDir path;
if (path.mkpath(p)) {
WriteLog("MakePath() mkpath returned true [" + p + "]");
if (path.exists()) {
WriteLog("MakePath() Path exists [" + p + "]");
msg = QString("Destination path [" + p + "] created");
}
else {
WriteLog("MakePath() Path does not exist [" + p + "]");
msg = QString("Unable to create destination path [" + p + "]");
return false;
}
}
else {
msg = QString("MakePath() mkpath returned false [" + p + "]");
return false;
}
return true;
}
The output from my program:
[2019/06/04 13:19:37][26034] MakePath() called with path [/usr2/archive/S0836VYL/6/5/dicom]
[2019/06/04 13:19:37][26034] MakePath() mkpath returned true [/usr2/archive/S0836VYL/6/5/dicom]
[2019/06/04 13:19:37][26034] MakePath() Path exists [/usr2/archive/S0836VYL/6/5/dicom]
and the output from the command line...
[onrc#ado2dev /]$ cd /usr2/archive/S0836VYL/6/5/dicom
-bash: cd: /usr2/archive/S0836VYL/6/5/dicom: No such file or directory
[onrc#ado2dev /]$
What am I missing??
Try to use this :
QString p("/usr2/archive/S1234ABC/5/6");
QDir d(p);
if(!d.exists() && !d.mkpath(p)) qDebug() << "Error: can't create folder '"<< p <<"'.";
else qDebug() << "Folder '"<< p <<"' exists or created successfully".
Hope it helps you.
All right, this is one for the record books...
The issue was a null terminator appended to the end of the S1234ABC string before being inserted into the database. That string was later used to create the above paths. That S1234ABC string was created using the following code:
QString prefix = "S";
QChar C1, C2, C3, etc... (randomly generated characters)
QString newID = prefix + C1 + C2 + etc...
This created a QString with a \0 on the end of it. Qt stored this value in the MySQL database, which I then pulled back into Qt and tried to make a path with it. Since it's a null terminated string, it appears normal on phpMyAdmin, in xterm, and the log files. Except... in puTTY in Windows, where I saw the weird path it was trying to create:
/usr2/archive/S0836VYL\u0000/10/3/dicom
Thankfully puTTY displayed the actual unicode value instead of ignoring it. Thank you putty! I never, ever would've figured that out...
Recreating the S1234ABC string using QStrings for each character instead of QChar fixed the issue. Now I have regular old strings in the database, and normal paths.
I am trying to make a save system for my ESP32 project, and I have the following code:
void write_string_nvs(char *memorySlot, String key, String value)
{
nvs_handle my_handle;
esp_err_t err = nvs_open(memorySlot, NVS_READWRITE, &my_handle);
if (err == ESP_OK)
{
int kL = key.length();
int vL = value.length();
char keyA[kL + 1];
key.toCharArray(keyA, kL + 1);
char valueA[vL + 1];
value.toCharArray(valueA, vL + 1);
Serial.println("Storing \"" + String(keyA) + "\"(" + String(kL) + ")>\"" + String(valueA) + "\"(" + String(vL) + ") in NVS.");
esp_err_t err = nvs_set_blob(my_handle, keyA, &valueA, vL);
if (err == ESP_OK)
{
err = nvs_commit(my_handle);
if (err == ESP_OK)
Serial.println("Correctly saved \"" + key + "\" in " + String(memorySlot));
else
Serial.println("write_string_nvs::commit -> Could not save \"" + key + "\" in " + String(memorySlot) + ": " + esp_err_toString(err, true));
}
else
Serial.println("write_string_nvs::nvs_set_blob -> Could not save \"" + key + "\" in " + String(memorySlot) + ": " + esp_err_toString(err, true) + "");
nvs_close(my_handle);
}
else
Serial.println("Could not initialize " + String(memorySlot) + " NVS slot: " + esp_err_toString(err, true) + "");
}
I call it the following way, from a serial command:
...
String params[3];
split(serialRead, ' ', params);
String s = params[0];
String k = params[1];
String v = params[2];
bool error = false;
if (s.length() <= 0) {
error = true;
Serial.println("Please, specify an storage name");
}
if (k.length() <= 0) {
error = true;
Serial.println("Please, specify a key");
}
if (v.length() <= 0) {
error = true;
Serial.println("Please, specify a value");
}
if (!error) {
String slotName = "";
if (startsWithIgnoreCase(s, "main")) {
slotName = "storage";
}
if (startsWithIgnoreCase(s, "wifi")) {
slotName = "wifi";
}
if (slotName.length() > 1) {
Serial.println("Writing \"" + v + "\"" + " at \"\"" + k + "\" in " + slotName);
char slot[slotName.length()];
slotName.toCharArray(slot, slotName.length());
write_string_nvs(slot, k, v);
} else
Serial.println("Specified invalid slot");
}
By doing this I am trying to make a command parser to store values and read them afterwards, with the following commands: storage write <wifi/main> <key> <value> and storage read <wifi/main> <key>.
But the problem comes when I try to type the write command, and the code executes, the ESP32 Serial returns:
assertion "heap != NULL && "realloc() pointer is outside heap areas"" failed: file "/Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/heap/heap_caps.c", line 285, function: heap_caps_realloc
abort() was called at PC 0x40152903 on core 1
Backtrace: 0x40091ca4:0x3ffce0c0 0x40091ed5:0x3ffce0e0 0x40152903:0x3ffce100 0x400847a9:0x3ffce130 0x4008483d:0x3ffce150 0x4008b2e9:0x3ffce170 0x4000bedd:0x3ffce190 0x400dd4e2:0x3ffce1b0 0x400dd544:0x3ffce1d0 0x400dd6a6:0x3ffce1f0 0x400dd6d1:0x3ffce210 0x400d1b06:0x3ffce240 0x400d5939:0x3ffce260 0x400de489:0x3ffce7d0 0x40094135:0x3ffce7f0
Rebooting...
I don't know what to do, I have tried some different write and read codes, but I can't find any that stores the values correctly. The read command works, but obviously, it doesn't return anything, because the memory is empty. Here's the read command, in case you want to take a look at it:
String read_string_nvs(char *memorySlot, String key)
{
nvs_handle my_handle;
esp_err_t err = nvs_open(memorySlot, NVS_READWRITE, &my_handle);
String espErrStr = esp_err_toString(err, true);
char *value;
if (err == ESP_OK || startsWithIgnoreCase(espErrStr, "ESP_OK"))
{
size_t string_size;
int kL = key.length();
char wifi_slot[kL + 1];
key.toCharArray(wifi_slot, kL + 1);
esp_err_t err = nvs_get_str(my_handle, wifi_slot, NULL, &string_size);
value = (char *)malloc(string_size);
err = nvs_get_str(my_handle, wifi_slot, value, &string_size);
nvs_close(my_handle);
return String(value);
}
else
Serial.println("Could not open memory (\"" + espErrStr + "\")");
return espErrStr;
}
I've been with this issue for some weeks, and I really don't know what to do, maybe the system is not good for what I want, or I may be doing something wrong.
For developing I am using VSCode with PlatformIO.
Please, take a look and it and if you could tell me what's wrong or what to do, I'd be really pleased.
Thanks in advance.
I am busy with the same problem (I am going to use 4Mb of the flash as a nvs partition) and I have found a some clue: https://www.esp32.com/viewtopic.php?t=6815
It seems that problem is with the RAM-size - the system needs a RAM to create the nvs-pages-map and if it's not enough for this task - it calls the system abort.
P.S. I have decoded my firmware.elf into firmware.lst with the addresses and the assembler code and the backtrace is so:
app_main -> initArduino -> nvs_flash_init -> nvs_flash_init_partition -> nvs_flash_init_custom -> ZN3nvs_storage_init_Ejj ->ZN3nvs_Storage_populateBlobIndicesIntrusiveList -> _Znwj -> _cxa_allocate_exception -> terminatev -> cxabiv111_terminateEPFvvE - here the system aborts
To decode the .elf into .lst - just copy the firmware.elf into the folder with the xtensa-esp32-elf-objdump.exe (it is probably here .platformio\packages\toolchain-xtensa32\bin) and run in the command prompt - xtensa-esp32-elf-objdump.exe -S -l -d firmware.elf > [YourFileName].lst
These lines are problematic:
char slot[slotName.length()];
slotName.toCharArray(slot, slotName.length());
write_string_nvs(slot, k, v);
slotName.length() will return the number of characters in slotName. slot is a C string, which needs a null terminating character at the end (\0), so it needs to be declared with one byte more than the number of characters in the string. The declaration you have is too short.
You can side-step the problem by rewriting these lines as:
write_string_nvs(slotName.c_str(), k, v);
String already stores its contents as a C string internally, so the c_str() method just gives you a pointer to the buffer it manages. Be careful with this, that pointer won't be valid after the String object becomes invalid, so if the String is a variable in a function or code block, its c_str() will stop being valid when that you leave that function or code block.
Since this is some kind of heap or memory allocation issue it's possible the bug is outside of the code you shared. I would review all the code looking for instances of where you convert a String to a C character array and try using the c_str() idiom instead.
This is a pretty common problem that bites a lot of programmers.
It's also possible the problem is in your write_string_nvs() implementation.
I made a console application which detects plugin and plugout events for all type of usb devices. but I wanted some filteration in it like I wanted to detect only webcams . This was done by using GUID class. The class for webcam is 'Image' class with GUID "{6bdd1fc5-810f-11d0-bec7-08002be2092f}" .The problem is that this 'Image' class is also used for scanners and I dont want to detect scanners.The code is given below:
static void Main(string[] args)
{
WqlEventQuery weqQuery = new WqlEventQuery();
weqQuery.EventClassName = "__InstanceOperationEvent";
weqQuery.WithinInterval = new TimeSpan(0, 0, 3);
weqQuery.Condition = #"TargetInstance ISA 'Win32_PnPEntity'";
ManagementEventWatcher m_mewWatcher = new ManagementEventWatcher(weqQuery);
m_mewWatcher.EventArrived += new EventArrivedEventHandler(m_mewWatcher_EventArrived);
m_mewWatcher.Start();
Console.ReadLine();
}
static void m_mewWatcher_EventArrived(object sender, EventArrivedEventArgs e)
{
bool bUSBEvent = false;
string deviceCaption = "";
string deviceType = "";
foreach (PropertyData pdData in e.NewEvent.Properties)
{
try
{
ManagementBaseObject mbo = (ManagementBaseObject)pdData.Value;
if (mbo != null)
{
foreach (PropertyData pdDataSub in mbo.Properties)
{
Console.WriteLine(pdDataSub.Name + " = " + pdDataSub.Value);
if (pdDataSub.Name == "Caption")
{
deviceCaption = pdDataSub.Value.ToString();
}
if (pdDataSub.Name == "ClassGuid" && pdDataSub.Value.ToString() == "{6bdd1fc5-810f-11d0-bec7-08002be2092f}")
{
bUSBEvent = true;
deviceType = "Image";
}
}
if (bUSBEvent)
{
if (e.NewEvent.ClassPath.ClassName == "__InstanceCreationEvent")
{
Console.WriteLine("A " + deviceType + " device " + deviceCaption + " was plugged in at " + DateTime.Now.ToString());
}
else if (e.NewEvent.ClassPath.ClassName == "__InstanceDeletionEvent")
{
Console.WriteLine("A " + deviceType + " device " + deviceCaption + " was plugged out at " + DateTime.Now.ToString());
}
}
}
}
catch (Exception ex)
{
}
}
}
for references check this link
I waited but no body answered this question so, after seeing all properties of ManagementBaseObject I found that there is a property named Service which is different for scanners. In scanners the value of Service property is usbscan while in cameras it is usbvideo.
eg.
you can do something like this
if (mbo.Properties["Service"].Value.ToString() == "usbscan")
{
//then it means it is a scanner
}
else
{
//then it means it is a camera
}
note: The main question was that how can we differentiate between a scanner and a webcam because they both use same GUID.