Post your code!

User avatar
Stix
Member
 
Posts: 1379
Joined: Fri Aug 04, 2017 14:19
Location: USA
IRC: nil
In-game: Stix [+alts]

Re: Post your code!

by Stix » Sun Jan 13, 2019 23:13

I edited my previous program to fix some bugs and colorize the health status information.

Example:
High-health = colorize green.
Medium-health = colorize yellow.
Low-health = colorize red.

And here's the updated source-code:
Code: Select all
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define RED   "\x1B[31m"
#define GRN   "\x1B[32m"
#define YEL   "\x1B[33m"
#define RESET "\x1B[0m"

int hunger;
int food;
int thirst;
int water;
int medkit;
int day;
int choice;
int amount;
int turn;
int status;
int status1;
int condition;
int item_value;
int situation;
int health;
int energy;
int attack;
int recover;
int enemy_health;
int enemy_energy;
int enemy_attack;
int enemy_recover;
char death[100];
char item[100];
char situation1[100];
char choice1[100];
char choice2[100];
char choice3[100];
char choice4[100];

int defaults() //stores defaults values that are called at th beginning of the program
{
  status1 = 0;
  status = 0;
  day = 0;
  health = 10;
  hunger = 0;
  thirst = 0;
  food = 5;
  water = 3;
  medkit = 1;
}

int fight_defaults() //stores default values for the functions fight()
{
  enemy_health = 10;
  enemy_energy = 4;
  enemy_attack = 3;
  enemy_recover = 2;
 
  energy = 4;
  attack = 3;
  recover = 2;
}

int drink()
{
  if(water < 1)
    printf("\nYou have no more water to drink.\n");
  else
    if(thirst > 0)
      {
   thirst -= 5;
   water--;
      }
  if(thirst < 0)
    thirst = 0;
}

int eat()
{
  if(food < 1)
    printf("\nYou have no more food to eat.\n");
  else
    if(hunger > 0)
      {
   hunger -= 3;
   food--;
      }
  if(hunger < 0)
    hunger = 0;
}

int do_nothing() //a function that does nothing (hack)
{
  status = status;
}

int heal()
{
  if(medkit < 1)
    printf("\nYou don't have any medkits.\n");
  else 
    if(health < 10)
      {
   printf("\nYou healed.\n");
   health += 3;
   medkit--;
      }
    else
      printf("\nYou are already at full health.\n");
}

int health_bar()
{
  if(condition == 0)
    {
      if(health <= 3)
   printf(RED "\nHealth: %d out of 10"RESET, health);
      if(health > 3)
   {
     if(health < 7)
       printf(YEL "\nHealth: %d out of 10"RESET, health);
   }
      if(health > 6)
   printf(GRN "\nHealth: %d out of 10"RESET, health);
    }
     
  if(condition == 1)
    {
      if(health <= 3)
   printf(RED "\n Health: %d" RESET, health);
      if(health > 3)
   {
     if(health < 7)
       printf(YEL "\n Health: %d" RESET, health);
   }
      if(health > 6)
   printf(GRN "\n Health: %d" RESET, health);
    }
 
  if(condition == 2)
    {
      if(enemy_health <= 3)
   printf(RED "           Health: %d" RESET, enemy_health);
      if(enemy_health > 3)
   {
     if(enemy_health < 7)
       printf(YEL "           Health: %d" RESET, enemy_health);
   }
      if(enemy_health > 6)
   printf(GRN "           Health: %d" RESET, enemy_health);
    }
}
     
int choices() //a function that gives a set of choices based on the current situation
{
  int r2 = rand() % 3;
  int r3 = rand() % 5;
  int r4 = rand() % 2;
 
  if(situation == 1)
    {
      if(choice == 1)
   drink();
      if(choice == 2)
   eat();
      if(choice == 3)
   do_nothing;
      if(choice == 4)
   heal();
      if(choice == 5)
   status = 1;
    }
 
  if(situation == 2)
    {
      switch(choice)
   {
   case 1:
     printf("\nYou started a fight!\n");
     fight();
     break;
   case 2:       
     if(r2 == 2)
       {
         printf("\nYou got attacked by a stranger!\n");
         fight();
       }
     if(r2 <= 1)
       {
         printf("\nYou ran away from a fight.\n");
         do_nothing();
       }
     break;
   case 3:      
     if(r4 == 0)
       break;
     if(r4 == 1)
       printf("\nYou got attacked by a stranger!\n");
     fight();
     break;
   case 4:
     do_nothing();
     break;
   case 5:
     status = 1;
     break;
   }
    }
 
  if(situation == 3)
    {         
      if(r3 == 0)
   {
     strcpy(item, "food-item");
     item_value = 1;
   }
      if(r3 == 1)
   {
     strcpy(item, "water-bottle");
     item_value = 2;
   }
      if(r3 == 2)
   {
     strcpy(item, "medkit");
     item_value = 3;
   } 
      if(r3 >= 3)
   printf("\nThe box was empty!\n");
               
      amount = r3;
   
      if(r3 <= 2)
   {   
     if(choice == 1)
       {
         printf("\nYou found %d %s(s).\n", amount, item);
        
         if(item_value == 1)
      food += amount;
         if(item_value == 2)
      water += amount;
         if(item_value == 3)
      medkit += amount;
       }
   }
     
      if(choice == 2)
   do_nothing();
      if(choice == 3)
   do_nothing();
      if(choice == 4)
   do_nothing();
      if(choice == 5)
   status = 1;
    }
}


int situations() //a function that randomly picks a situation to be current
{
  int r = rand() % 5; //grabbing a random number (0-4)

  if(r <= 2)
    {
      strcpy(situation1, "You are walking through an abandoned town."); //setting situation
      strcpy(choice1, "Drink."); //setting choices for the situation
      strcpy(choice2, "Eat.");
      strcpy(choice3, "Do nothing.");
      strcpy(choice4, "Heal.");
      situation = 1;
    }
 
  if(r == 3)
    {
      strcpy(situation1, "While walking you meet a stranger."); //setting situation
      strcpy(choice1, "Fight."); //setting choices for the situation
      strcpy(choice2, "Run.");
      strcpy(choice3, "Do nothing.");
      strcpy(choice4, " ");
      situation = 2;
    }

  if(r == 4)
    {
      strcpy(situation1, "While walking you find a box."); //setting situation
      strcpy(choice1, "Open it."); //setting choices for the situation
      strcpy(choice2, "Do nothing.");
      strcpy(choice3, " ");
      strcpy(choice4, " ");
      situation = 3;
    }
}

int fight() //a function that handles fighting between the player and enemy ai
{
  fight_defaults();
 
  while(1) //loop repeats until a 'break' is called
    {
      printf("\n>>>>>>>STATS<<<<<<<");
      printf("  >>>>>>>ENEMY<<<<<<<");
      printf("\n*******************");
      printf("  *******************");
      condition = 1;
      health_bar();
      condition = 2;
      health_bar();
      condition = 0;
      printf("\n Energy: %d        ", energy);
      printf("   Energy: %d         ", enemy_energy); 
      printf("\n Attack: %d        ", attack);   
      printf("   Attack: %d         ", enemy_attack);
      printf("\n Recover: %d       ", recover);
      printf("   Recover: %d        ", enemy_recover);
      printf("\n*******************");
      printf("  *******************\n");
     
      turn = 0; //sets turn to players beginning of every loop
     
      if(turn == 0) //takes input if conditions are met
   {
     printf("\nYour turn!\n");
    
     while(turn == 0)
       {
         printf("\n1) Fight.\n2) Recover.\n3) Skip turn.\n");
         scanf("%d", &choice);
        
         if(choice == 1) //calculates the input and makes decisions based on it     
      {
        if(energy >= 1)
          {
            enemy_health += enemy_recover - attack; //attacking enemy
            energy -= 1;
            break; //breaking the loop
          }
        else
          printf("\nYou dont have enough energy to attack!\n");
        continue; //restarting the loop
      }
        
         if(choice == 2)
      energy += recover; //recover energy
         break;
         if(choice == 3)
      do_nothing(); //the if statement executes nothing (hack)
         break;           
       }                      
   }
 
      if(enemy_health < 1) //checking if the player won the fight
   {
     printf("\nYou won the fight!\n");
     break;
   }
     
      death_event();

      if(status1 == 1)
   break;
     
      turn = 1; //sets turn to enemy() function end of every loop
      enemy();
    }
}

int enemy() //a function that takes a number (0-2) and makes decisions based on the result
{
  if(turn == 1)
    {
      printf("\nEnemy's turn!");
      int r = rand() % 3; //grabbing a number (0-2)
     
      switch(r)
   {
   case 0:
     /*fall through (which makes the enemy more likely to attack)*/
   case 1:   
     if(enemy_energy >= 1)
       {
         health += recover - enemy_attack; //attacking player
         enemy_energy -= 1;
         printf("\n\nThe enemy attacked!\n");
         break;
       }
     /*fall through (so the enemy has to recover if they're unable to attack)*/
   case 2:   
     enemy_energy += enemy_recover; //recovering energy
     printf("\n\nThe enemy recovered energy.\n");
     break;
   }
    }


int death_event() //Computes cause of death and prints the death-message accordingly.
{
  if(hunger > 10) //checks if the player has starved
    {
      status = 3;
      strcpy(death, "hunger");
    }
  if(thirst > 10) //checks if the player is dehydrated
    {
      status = 3;
      strcpy(death, "thirst");
    }
  if(health < 1) //checks if the player was killed by the enemy ai
    {
      status = 3;
      strcpy(death, "a stranger");
    }
  if(status == 3)
    {
      /*notifying the user of their characters death*/
      printf("\nDeath by %s, you survived %d days. ", death, day);
      status = 1;
      printf("Press \'1\' to restart or \'2\' to quit: ");
      scanf("%d", &choice);
     
      if(choice == 1)
   {
     status = 2;
     status1 = 1;
   }
     
      if(choice == 2)
   {
     status = 1;
     status1 = 1;
   }
     
      if(choice > 2)
   {    
     /*informing the user of an invalid number for input*/
     printf("\nError: to high a value.\n\n");
     status = 1;
   }
    }
}


int win() //checks if the user wins and notifies them accordingly
{
  if(day > 29)
    printf("\nCongratulations, you survived all %d days!\n\n", day);
}


main()
{
 
  defaults(); //grabbing default values
 
  for(day = 1; day < 31; day++) //a loop that will run until day == 30
    {
      situations(); //executing a function that randomly sets the situation
     
      printf("\nDay %d: %s\n", day, situation1);
      printf("\n>>>>>>>>>>>>>>>>>>STATS<<<<<<<<<<<<<<<<<<");
      printf("\n*****************************************");
      health_bar();
      printf("\nHunger: %d out of 10", hunger);
      printf("\nThirst: %d out of 10", thirst);
      printf("\n\nYou have %d water-bottle(s).", water);
      printf("\nYou have %d food-item(s).", food);
      printf("\nYou have %d medkit(s).", medkit);
      printf("\n*****************************************\n");
      printf("\nShould you:\n1) %s\n2) %s\n3) %s\n4) %s\n", choice1, choice2, choice3, choice4);
     
      scanf("%d", &choice);
     
      /*executing a function that will make decisions based on the current situation and
        the users input*/
      choices();

      if(situation == 1) //incrementing hunger/thirst every loop if the condition is met
   {
     hunger++;
     thirst+=2;
   }

      if(status == 1)
   break;
      if(status == 2)
   defaults(); //grabbing default values
      continue;

      death_event(); //executing a function that checks if the player died
      win(); //executing a function that will check if the user won
     
      if(status == 1)
   break;
      if(status == 2)
   defaults(); //grabbing default values
      continue;
    }

  return(0);
}
Hey, what can i say? I'm the bad guy.
 

User avatar
sorcerykid
Member
 
Posts: 1172
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Mon Jan 14, 2019 11:57

I have a habit of going through code and finding astounding amounts of extraneous logic. When it comes to senseless redundancy in programming, I'll admit I'm very anal retentive. Nearly half the mods used on JT2 have been refactored or rewritten from scratch, ultimately culling hundreds of lines. Here's a section of code from Zorman3000's pathfinder API that I recently cleaned up:

I managed to reduce this:
Code: Select all
  local start_x_sign = (start_pos.x - end_pos.x) / math.abs(start_pos.x - end_pos.x)
  local start_z_sign = (start_pos.z - end_pos.z) / math.abs(start_pos.z - end_pos.z)
  local end_x_sign = (end_pos.x - start_pos.x) / math.abs(end_pos.x - start_pos.x)
  local end_z_sign = (end_pos.z - start_pos.z) / math.abs(end_pos.z - start_pos.z)

  -- Correct the signs if they are nan
  if math.abs(start_pos.x - end_pos.x) == 0 then
    start_x_sign = -1
    end_x_sign = 1
  end
  if math.abs(start_pos.z - end_pos.z) == 0 then
    start_z_sign = -1
    end_z_sign = 1
  end


down to only this:
Code: Select all
local base_x_sign = base.pos.x <= goal_pos.x and -1 or 1
local goal_x_sign = -base_x_sign
local base_z_sign = base_pos.z <= goal_pos.z and -1 or 1
local goal_z_sign = -base_z_sign
 

User avatar
Hybrid Dog
Member
 
Posts: 2726
Joined: Thu Nov 01, 2012 12:46

Re: Post your code!

by Hybrid Dog » Sat Jan 19, 2019 14:19

I've written code for nearly spherical lighting.
Minetest default:
Image
Spherical:
Image
I didn't make a 3d version and haven't implemented in minetest yet.
https://github.com/minetest/minetest/is ... -446650534
I don't know the performance impact. It uses vectors instead of scalars for light spreading.
Code: Select all
#include <stdlib.h> // malloc, EXIT_*
#include <string.h> // memset
#include <math.h>
#include <png.h>

#define EXIT_PNG(F) if (!F) { \
   fprintf(stderr, "%s\n", bild.message); \
   return EXIT_FAILURE; \
}

#define CLAMP(V, A, B) (V) < (A) ? (A) : (V) > (B) ? (B) : (V)

#define u8 unsigned char

struct pixel {
   u8 r;
   u8 g;
   u8 b;
   u8 a;
};
#define PIXELBYTES 4

struct matrix {
   int w;
   int h;
   float *data;
};

struct image {
   int w;
   int h;
   struct pixel *pixels;
};

/*! \brief get y, cb and cr values each in [0;1] from u8 r, g and b values
 *
 * there's gamma correction,
 * see http://www.ericbrasseur.org/gamma.html?i=1#Assume_a_gamma_of_2.2
 * 0.5 is added to cb and cr to have them in [0;1]
 */
static void rgb2ycbcr(u8 or, u8 og, u8 ob, float *y, float *cb, float *cr)
{
   float divider = 1.0f / 255.0f;
   float r = powf(or * divider, 2.2f);
   float g = powf(og * divider, 2.2f);
   float b = powf(ob * divider, 2.2f);
   *y = (0.299f * r + 0.587f * g + 0.114f * b);
   *cb = (-0.168736f * r - 0.331264f * g + 0.5f * b) + 0.5f;
   *cr = (0.5f * r - 0.418688f * g - 0.081312f * b) + 0.5f;
}

/*! \brief the inverse of the function above
 *
 * numbers from http://www.equasys.de/colorconversion.html
 * if values are too big or small, they're clamped
 */
static void ycbcr2rgb(float y, float cb, float cr, u8 *r, u8 *g, u8 *b)
{
   float vr = (y + 1.402f * (cr - 0.5f));
   float vg = (y - 0.344136f * (cb - 0.5f) - 0.714136f * (cr - 0.5f));
   float vb = (y + 1.772f * (cb - 0.5f));
   float exponent = 1.0f / 2.2f;
   vr = powf(vr, exponent);
   vg = powf(vg, exponent);
   vb = powf(vb, exponent);
   *r = CLAMP(vr * 255.0f, 0, 255);
   *g = CLAMP(vg * 255.0f, 0, 255);
   *b = CLAMP(vb * 255.0f, 0, 255);
}

/*! \brief Convert an rgba image to 4 ycbcr matrices with values in [0, 1]
 */
static struct matrix *image_to_matrices(struct image *bild)
{
   int w = bild->w;
   int h = bild->h;
   struct matrix *matrices = malloc(
      PIXELBYTES * sizeof(struct matrix));
   for (int i = 0; i < PIXELBYTES; ++i) {
      matrices[i].w = w;
      matrices[i].h = h;
      matrices[i].data = malloc(w * h * sizeof(float));
   }
   for (int i = 0; i < w * h; ++i) {
      struct pixel px = bild->pixels[i];
      // put y, cb, cr and transpatency into the matrices
      rgb2ycbcr(px.r, px.g, px.b,
         &matrices[0].data[i], &matrices[1].data[i], &matrices[2].data[i]);
      float divider = 1.0f / 255.0f;
      matrices[3].data[i] = px.a * divider;
   }
   return matrices;
}

/*! \brief Convert 4 matrices to an rgba image
 *
 * Note that matrices becomes freed.
 */
static struct image *matrices_to_image(struct matrix *matrices)
{
   struct image *bild = malloc(sizeof(struct image));
   int w = matrices[0].w;
   int h = matrices[0].h;
   bild->w = w;
   bild->h = h;
   struct pixel *pixels = malloc(w * h * PIXELBYTES);
   for (int i = 0; i < w * h; ++i) {
      struct pixel *px = &pixels[i];
      ycbcr2rgb(matrices[0].data[i], matrices[1].data[i], matrices[2].data[i],
         &px->r, &px->g, &px->b);
      //~ float a = matrices[3].data[i] * 255;
      //~ px->a = CLAMP(a, 0, 255);
      px->a = 255;
   }
   for (int i = 0; i < PIXELBYTES; ++i) {
      free(matrices[i].data);
   }
   free(matrices);
   bild->pixels = pixels;
   return bild;
}

/*! \brief Here my algorithm resides
 *
 * \param mat The 4 matrices obtained form image_to_matrices.
 * \param s The input parameter.
 */
static void apply_algorithm(struct matrix *mat, int s)
{
   // preparation
   int w = mat->w; // input width
   int h = mat->h;
   float *input = mat->data;
   int w2 = w; // output width
   int h2 = h;
   int input_size = w * h * sizeof(float);
   int output_size = input_size;
   float *d = malloc(output_size);

////////////////////////////////////////////////////////////////////////////////

   // y x offset values:
   // 1 -1, 1 1, -1 1, -1 -1
#define YOFF(V) (1 - ((V) & 2))
#define XOFF(V, YO) ((YO) * ((((V) << 1) & 2) - 1))

   struct light {
      float vals[4];
   };

   // Initialize light map
   int lm_w = w+2;
   int lm_h = h+2;
   struct light lightmap[(lm_h)*lm_w + lm_w];
   memset(lightmap, 0, lm_w * lm_h * sizeof(struct light));

   // Initialize blockage map
   u8 blocked[(lm_h)*lm_w + lm_w];
   memset(lightmap, 0, lm_w * lm_h);
   for (int y = 1; y < h+1; ++y) {
      for (int x = 1; x < w+1; ++x) {
         // Light is blocked where the input image is bright enough
         blocked[y*lm_w + x] = input[y*w + x] > 0.5f;
      }
   }


   // Add start light source in the middle
   struct light source = {{15.0, 15.0, 15.0, 15.0}};
   lightmap[(lm_h/2)*lm_w + lm_w/2] = source;

   // Spread light until nothing changes
   const float sqrt2 = sqrt(2.0);
   int spread_cnt = 5318008;
   while (spread_cnt > 0) {
      spread_cnt = 0;
      for (int y = 1; y < h+1; ++y) {
         for (int x = 1; x < w+1; ++x) {
            struct light *l = &lightmap[y*lm_w + x];
            for (int v = 0; v < 4; ++v) {
               float light = l->vals[v];
               if (light == 0)
                  // No light for this direction
                  continue;

               int y_off = YOFF(v);
               int x_off = XOFF(v, y_off);
               int spread_before = spread_cnt;

               // Propagate horizontally and vertically or the other way
               // round
               float light_neigh = light - 1.0f;
               // Get the vector pointing 90° left to where v points to
               int v_left = (v + 3) % 4;
               int y_off_left = YOFF(v_left);
               int x_off_left = XOFF(v_left, y_off_left);
               // Get the middle between where v and v_left points to,
               // this is the horizontal or vertical neighbour left to
               // the diagonal neighbour from before
               y_off_left = (y_off_left + y_off) / 2;
               x_off_left = (x_off_left + x_off) / 2;
               int i_left = (y+y_off_left)*lm_w + x+x_off_left;
               if (!blocked[i_left]
                     && light_neigh > lightmap[i_left].vals[v]) {
                  // Update the light for the same vector but for the
                  // horizontal or vertical neighbour
                  lightmap[i_left].vals[v] = light_neigh;
                  ++spread_cnt;
               }
               // Analogous for the right one
               int v_right = (v + 1) % 4;
               int y_off_right = YOFF(v_right);
               int x_off_right = XOFF(v_right, y_off_right);
               y_off_right = (y_off_right + y_off) / 2;
               x_off_right = (x_off_right + x_off) / 2;
               int i_right = (y+y_off_right)*lm_w + x+x_off_right;
               if (!blocked[i_right]
                     && light_neigh > lightmap[i_right].vals[v]) {
                  // Update the light for the same vector but for the
                  // horizontal or vertical neighbour
                  lightmap[i_right].vals[v] = light_neigh;
                  ++spread_cnt;
               }

               // Propagate diagonally
               // Do not propagate if left and right were blocked
               int i_diag = (y+y_off)*lm_w + x+x_off;
               float light_diag = light - sqrt2;
               if (!blocked[i_diag] && spread_before < spread_cnt
                     && light_diag > lightmap[i_diag].vals[v]) {
                  // Update the light for the same vector but for the
                  // diagonal neighbour
                  lightmap[i_diag].vals[v] = light_diag;
                  ++spread_cnt;
               }
            }
         }
      }
      fprintf(stderr, "%d lights spread.\n", spread_cnt);
   }

   // Move the light to the picture

   int i = 0;
   for (int y = 0; y < h; ++y) {
      for (int x = 0; x < w; ++x) {
         float light = 0.0;
         struct light *l = &lightmap[(y+1)*lm_w + x+1];
         for (int v = 0; v < 4; ++v) {
            if (light < l->vals[v])
               light = l->vals[v];
         }
         int light_quantized = light + 0.5f;
         d[i++] = light_quantized / 15.0f;
      }
   }

////////////////////////////////////////////////////////////////////////////////

   // update the matrix
   mat->data = d;
   mat->w = w2;
   mat->h = h2;

   // tidy up
   free(input);
}

/*! \brief applies the algorithm on each matrix
 *
 * \param bild The image, it's content is changed when finished.
 * \param s Must be a natural number.
 */
void do_an_image(struct image **bild, int s)
{
   struct matrix *matrices = image_to_matrices(*bild);
   //~ for (int i = 0; i < PIXELBYTES; ++i) {
   for (int i = 0; i < 1; ++i) {
      apply_algorithm(&(matrices[i]), s);
   }
   *bild = matrices_to_image(matrices);
}

int main(int argc, char **args)
{
   if (argc != 2) {
      fprintf(stderr, "Missing arguments, usage: <cmdname> "
         "<input>\n");
      return EXIT_FAILURE;
   }
   int input = atoi(args[1]);
   if (input < 2) {
      fprintf(stderr, "Invalid: %d\n",
         input);
      return EXIT_FAILURE;
   }

   png_image bild;
   memset(&bild, 0, sizeof(bild));
   bild.version = PNG_IMAGE_VERSION;
   EXIT_PNG(png_image_begin_read_from_stdio(&bild, stdin))

   int w = bild.width;
   int h = bild.height;
   bild.flags = PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB;
   bild.format = PNG_FORMAT_RGBA;
   struct pixel *pixels = malloc(w * h * 4);
   EXIT_PNG(png_image_finish_read(&bild, NULL, pixels, 0, NULL))


   struct image origpic = {w = w, h = h, pixels = pixels};
   struct image *newpic = &origpic;
   do_an_image(&newpic, input);
   bild.width = newpic->w;
   bild.height = newpic->h;
   free(pixels);
   pixels = newpic->pixels;
   free(newpic);


   EXIT_PNG(png_image_write_to_stdio(&bild, stdout, 0, pixels, 0, NULL));
   free(pixels); // redundant free to feed valgrind
   return EXIT_SUCCESS;
}

‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪
 

User avatar
sorcerykid
Member
 
Posts: 1172
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Sat Jan 19, 2019 16:30

Nice work HybridDog, I presume lighting calculations are handled server side?

Here's a quick one-liner tcsh alias for creating file backups. The "bak" command will maintain a sequential file history with permissions and timestamps in tact. This can come in useful when editing config files or testing lua scripts, without the need for a full fledged git repository.

Usage is very straightforward:

    % bak minetest 3 conf
In this case "minetest" is the base file name, 3 is the next number in the sequence, and "conf" is the extension. The file named "minetest.conf" will be thus copied using option -ip to "minetest.old3.conf" in the same directory. You will be given a warning if the sequence number already exists, so there's no worry about accidental clobbering.

Just add the following two lines to ~/.tcshrc, and you're ready to go!

Code: Select all
alias bak 'cp -ip \!:1.\!:3 \!:1.bak\!:2.\!:3'
alias old 'cp -ip \!:1.\!:3 \!:1.old\!:2.\!:3'


Of course, with some simple tweaks to the alias, it would also be possible to archive the backups into a subdirectory such as "_old" or even to tarball them. And if you want to get really fancy, you could automate the file numbering as follows:

Code: Select all
set DOLLAR='$'
alias bak '/bin/ls -1v \!:1.bak*.\!:2 | tail -n 1 | awk -F"." "{printf substr(${DOLLAR}2,4)+1}" | xargs -0 -ISEQ cp -ip \!:1.\!:2 \!:1.bakSEQ.\!:2'


Now it's as simple as typing bak minetest conf, to backup the minetest configuration file.

Note that this requires creating an environment variable $DOLLAR, due to the quirky rules about interpolation within double quoted strings.
 

User avatar
joe7575
Member
 
Posts: 341
Joined: Mon Apr 24, 2017 20:38
Location: Germany, in the deep south
GitHub: joe7575
In-game: JoSto

Re: Post your code!

by joe7575 » Sun Jan 20, 2019 11:26

Generate a HTML file with table of contents from 'lua_api.txt':
* install markdown2
* download 'lua_api.txt'
* run the script
Code: Select all
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-

# first install markdown2 via: pip install markdown2

import markdown2

header = u"""<!doctype html>
    <html>
        <head>
            <title>Lua Modding API Reference - Minetest Modding Book</title>
            <meta charset="UTF-8">
            <meta name="author" content="rubenwardy">
            <style>
                code { background-color:#F0F0F0;}
            </style>
        </head>
    <body>
        <h2>Table of Contents</h2>
""".encode("utf-8")

footer = """
    </body>
</html>""".encode("utf-8")

html = markdown2.markdown_path("./lua_api.txt", extras=["toc"])
toc = html.toc_html.encode("utf-8")
body = html.encode("utf-8")

file("./lua_api.html", "wt").write(header + toc + body + footer)
print "Ready."
Sent from my Commodore 64. My Mods: TechPack, Hyperloop, Tower Crane, Lumberjack, Iron Age, Entrance, Minecart, Signs Bot.
 

User avatar
sorcerykid
Member
 
Posts: 1172
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Mon May 27, 2019 04:11

Here's a string library function that supports conditional pattern matching with capture groups stored to the underscore variable.

Code: Select all
string.is_match = function( text, glob )
     -- use underscore variable to preserve captures
     _ = { string.match( text, glob ) )
     return #_ > 0
end


This is a simple example of how string.is_match() can be used:

Code: Select all
if string.is_match( input, "^load (%w+)$" ) or string.is_match( input, "^load (%w+%.txt)$" ) then
     print( "loading file " .. _[ 1 ] )
     -- code to load file
elseif string.is_match( input, "^save (%w+)$" ) or string.is_match( input, "^save (%w+%.txt)$" ) then
     print( "saving file " .. _[ 1 ] )
     -- code to save file
end
 

User avatar
Gael de Sailly
Member
 
Posts: 737
Joined: Sun Jan 26, 2014 17:01
Location: Lyon, France
GitHub: Gael-de-Sailly
IRC: Gael-de-Sailly
In-game: Gael-de-Sailly Ginkgoo

Re: Post your code!

by Gael de Sailly » Wed May 29, 2019 01:05

Tiny code snippet for CSV file parsing – if anyone need it.
Code: Select all
local function read_csv(path)
   local file = io.open(path, "r")
   local t = {}
   for line in file:lines() do
      if line:sub(1,1) ~= "#" and line:find("[^%,% ]") then
         table.insert(t, line:split(",", true))
      end
   end
   return t
end


Larger piece of code to load binary numbers, including floats, that will be needed for the next version of geo-mapgen.
Code: Select all
numberio = {}

-- Define number types

local ntypes = {
   int8    = {1, false, true},
   uint8   = {1, false, false},
   int16   = {2, false, true},
   uint16  = {2, false, false},
   float16 = {2, true, 5, 10},
   int32   = {4, false, true},
   uint32  = {4, false, false},
   float32 = {4, true, 8, 23},
   int64   = {8, false, true},
   uint64  = {8, false, false},
   float64 = {8, true, 11, 52},
}

for name, ntype in pairs(ntypes) do
   numberio[name] = ntype
end

-- Endianness

local bigendian = false

function numberio.get_endianness()
   return bigendian
end

function numberio.set_endianness(is_bigendian)
   bigendian = is_bigendian
end

local function reverse_table(t)
   local n = #t
   for i=1, math.floor(n/2) do
      t[i], t[n+1-i] = t[n+1-i], t[i]
   end
end

-- READ NUMBERS

local function read_bits(bytes, ...)
   local pattern = {...}
   if not bigendian then
      reverse_table(bytes)
   end
   local r = 8
   local b = table.remove(bytes, 1)
   local v = {}
   for _, nbits in ipairs(pattern) do
      local n = 0
      while nbits > r do
         nbits = nbits - r
         n = n + b*2^nbits
         r = 8
         b = table.remove(bytes, 1)
      end
      if nbits > 0 then
         local d = 2^(r-nbits)
         n = n + math.floor(b/d)
         b = b % d
         r = r - nbits
      end
      table.insert(v, n)
   end

   return unpack(v)
end

local function get_reader(ntype)
   local len, is_float, p1, p2 = unpack(ntype)

   local reader
   if is_float then -- Float
      local exp_offset = -2^(p1-1)+1
      local exp_max = 2^p1-1
      local denormal_factor = 2^(exp_offset+1)
      local mantissa_factor = 2^-p2
      reader = function(input)
         local sign, exp, mantissa = read_bits(input, 1, p1, p2)
         sign = (-1)^sign
         if exp == 0 then
            if mantissa == 0 then
               return sign * 0
            end
            return sign * denormal_factor * mantissa * mantissa_factor
         elseif exp == exp_max then
            if mantissa == 0 then
               return sign*math.huge
            else
               return sign < 0 and math.sqrt(-1) or -math.sqrt(-1)
            end
         end
         return sign * 2^(exp+exp_offset) * (1 + mantissa*mantissa_factor)
      end
   elseif p1 then -- Signed int
      local nbits = len*8
      local max = 2^(nbits-1)
      local decr = 2^nbits
      reader = function(input)
         local n = read_bits(input, nbits)
         if n >= max then
            return n - decr
         end
         return n
      end
   else -- Unsigned int
      local nbits = len*8
      reader = function(input)
         return read_bits(input, nbits)
      end
   end
   ntype.reader = reader
   return reader
end

function numberio.readnumber(input, ntype, pack)
   if type(ntype) == "string" then
      ntype = ntypes[ntype]
   end
   local reader = ntype.reader
   if not reader then
      reader = get_reader(ntype)
   end

   local len = ntype[1]
   local inputlen = math.floor(#input/len)

   if inputlen == 1 then
      local bytes = {input:byte(1, len)}
      if pack then
         return {reader(bytes)}
      else
         return reader(bytes)
      end
   else
      local v = {}
      local start = 1
      for i=1, inputlen do
         local stop = i*len
         local bytes = {input:byte(start, stop)}
         table.insert(v, reader(bytes))
         start = stop + 1
      end

      if pack then
         return v
      else
         return unpack(v)
      end
   end
end
Just realize how bored we would be if the world was perfect.
 

User avatar
sorcerykid
Member
 
Posts: 1172
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Fri Dec 27, 2019 22:42

Seasons greetings everyone!

Here is a handy Lua command line script for listing all nodes that are owned by specific players (like steel doors, locked chests, etc.) in your map database. Just change the following variables at the head of the script:

  • path - the path of the map.sqlite database to be read
  • area - the region of the map to be searched, specified as minimum and maximum mapblock positions
  • owner_name- the name of the owner to search for (nil will search for all owners)
  • node_name- the name of the owned nodes to search for (nil will search for all owned nodes)
Keep in mind, this script can take some time to process very large databases, so it is strongly recommended to limit the search area to no more than ~50 cubic mapblocks at a time.

Code: Select all
require( "maplib" )

local path = "/home/minetest/.minetest/worlds/world/map.sqlite"
local area = MapArea( { x = -25, y = -10, z = -25 }, { x = 25, y = 5, z = 25 } )
local node_name = "protector:protect"
local node_owner = "sorcerykid"

local map_db = MapDatabase( path, false )
print( "Creating cache..." )
map_db.create_cache( false )
print( "Examining database..." )

local function to_node_pos( idx, mapblock_idx )
        local mapblock_pos = mapblock_idx and decode_pos( mapblock_idx ) or 0
        local pos = { }

        pos.x = ( idx % 16 ) + mapblock_pos.x * 16
        idx = math.floor( idx /16 )
        pos.y = ( idx % 16 ) + mapblock_pos.y * 16
        idx = math.floor( idx /16 )
        pos.z = ( idx % 16 ) + mapblock_pos.z * 16

        return pos
end

local block_total = 0
local node_count = 0

for index, block in map_db.iterate_area( area ) do
        local nodemeta_list = block.get_nodemeta_list( )
        local nodename_map = block.nodename_map
        local node_list

        -- don't waste cpu cycles getting nodes unless there is meta
        if next( nodemeta_list ) then
                node_list = block.get_node_list( )
        end

        block_total = block_total + 1

        for i, m in pairs( nodemeta_list ) do
                local name = nodename_map[ node_list[ i + 1 ].id ]
                local owner = m.fields.owner

                if owner and ( not node_owner or owner == node_owner ) and ( not node_name or name == node_name ) then
                        node_count = node_count + 1

                        print( string.format( "%s owned by %s at (%s).",
                                name, owner, pos_to_string( to_node_pos( i, index ) )
                        ) )
                end
        end
end

print( string.format( "Found %d owned nodes (scanned %d map blocks).", node_count, block_total ) )
 

User avatar
sorcerykid
Member
 
Posts: 1172
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Tue Jan 07, 2020 02:30

Here is a helper function for concatenating the elements of a table that is comprised of subtables (or any other non-string value) by means of a join function.

str = table.join( list, sep, func )
Returns a string by concatenating the return values of a join function

  • list - the table to be concatenated
  • sep - the delimiter to separate the return values of the join function
  • func - the function to be executed for each element of the table, receiving the index and value as arguments and returning a string
Note that just like Lua's builtin table.concat( ) function, only indexed arrays are supported, not key-value stores.

Code: Select all
function table.join( list, sep, func )
        local str = ""
        for i, v in ipairs( list ) do
                local res = func( i, v )
                if res ~= nil then
                        str = i > 1 and str .. sep .. res or str .. res
                end
        end
        return str
end


This is particularly useful when you want to list all the values from a specific field in a database. Instead of iterating over every record and manually appending the delimeters, the entire operation can be performed in a single statement. For example, assuming that you have a database of players, each with fields "name" and "rank".

Code: Select all
-- OLD METHOD:
local str = ""
for i, v in ipairs( users ) do
     if i > 0 then
          str = str .. ","
     end
     str = str .. v.name .. "(" .. v.rank .. ")"
end

-- NEW METHOD
local str = table.join( users, ",", function ( i, v )
     return v.name .. "(" .. v.rank .. ")"
end )


Where this function truly excels, however, is when dynamically building formspec strings (as with the dropdown[] and textlist[] elements), since it mitigates the need to break apart a series of string concatenations simply to perform a loop.

Code: Select all
-- BEFORE
formspec = formspec
     .. "label[0.5,0.5;The following " .. #results .. " results were found:]"
     .. "textlist[0.5,1.0;3.5,4.0;results;"

     for i, v in pairs( results ) do
          formspec = ( i > 1 and formspec .. "," or formspec ) .. minetest.formspec_escape( v.label )
     end

     formspec = formspec .. string.format( ";%d;false]", result_idx )

-- AFTER
formspec = formspec
     .. "label[0.5,0.5;The following " .. #results .. " results were found:]"
     .. "textlist[0.5,1.0;3.5,4.0;results;"
     .. table.join( results, ",", function ( i, v ) return minetest.formspec_escape( v.label ) end )
     .. string.format( ";%d;false]", result_idx )
 

User avatar
sorcerykid
Member
 
Posts: 1172
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Thu Jan 09, 2020 02:06

Some more helper functions that are used in a lot of my mods since they can greatly simplify conditionals and assignments.

node = minetest.get_node_above( pos, off )
Returns the node directly above the position or at a specified vertical offset from the position (negative for below).

  • pos - the reference node position
  • off - the vertical offset from the position, defaults to 1

nval = math.clamp( val, min, max )
Returns the numeric value clamped to the lower and upper boundaries.

  • val - the value to be clamped
  • min - the lower boundary to clamp to
  • max - the upper boundary to clamp to
npos = vector.offset( pos, x, y, z )
Returns a vector offset along three axes from the starting vector. This is a less verbose alternative to vector.add( ) in which a vector must be supplied as the second argument.

  • pos - the starting vector position
  • x - the offset along the x-axis, or 0 for no offset
  • y - the offset along the y-axis, or 0 for no offset
  • z - the offset along the z-axis, or 0 for no offset
npos = vector.offset_y( pos, y )

Returns a vector offset along the y-axis from the starting vector. This is a convenient shorthand for vector.offset( ) with zero offset for both horizontal axes.

  • pos - the starting vector position
  • y - the offset along the y-axis, defaults to 1
Simply add the following code to builtin/common/misc_helpers.lua to make these functions available to all of your games:

Code: Select all
minetest.get_node_above = function ( pos, off )
        return minetest.get_node( { x = pos.x, y = pos.y + ( off or 1 ), z = pos.z } )
end

vector.offset = function ( pos, x, y, z )
        return { x = pos.x + x, y = pos.y + y, z = pos.z + z }
end

vector.offset_y = function ( pos, y )
        return { x = pos.x, y = pos.y + ( y or 1 ), z = pos.z }
end

math.clamp = function ( val, min, max )
        return val < min and min or val > max and max or val
end
Last edited by sorcerykid on Thu Jan 09, 2020 12:38, edited 1 time in total.
 

User avatar
orwell
Member
 
Posts: 803
Joined: Wed Jun 24, 2015 18:45
Location: Raxacoricofallapatorius
GitHub: orwell96
IRC: orwell96_mt
In-game: orwell

Re: Post your code!

by orwell » Thu Jan 09, 2020 07:51

Hey, you didn't post the actual code...
Lua is great!
List of my mods
I like singing. I like dancing. I like ... niyummm...
 

User avatar
Hybrid Dog
Member
 
Posts: 2726
Joined: Thu Nov 01, 2012 12:46

Re: Post your code!

by Hybrid Dog » Fri Jan 10, 2020 21:28

orwell wrote:Hey, you didn't post the actual code...

It is there now (probably the post was edited).



There is code and documentation for a Priority Queue and more: https://github.com/minetest/minetest/pull/7683/files
Testing has shown that a Binary Heap is faster than the Pairing Heap in practice in lua(jit).

‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪
 

User avatar
sorcerykid
Member
 
Posts: 1172
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Tue Jan 14, 2020 01:21

I created this handy function for debugging, but it can serve many other purposes for examining numeric values in Lua.

res = bits_format( str, ... )
Extends the string.format() function with an additional %b format specifier for outputting numbers in binary notation. The str is a string that consists of one or more format specifiers, each associated with an argument for substitution.

    %[flag][field-width][:group-width]b
The %b format specifier accepts a flag of either 0 to pad the field with leading zeroes or - to left-justify the field. With no flag, the field will be padded with leading spaces instead. If no field width is specified then the only the minimum digits necessary to represent the number in binary notation will be shown, with no leading spaces or zeros. If the field is not wide enough to accommodate the entire number in binary notation, then it will be extended (no digits will be truncated). Sets of bits may also be evenly spaced provided an optional group width.

Code: Select all
local function bits_format( str, ... )
        local args = { ... }

        local function parse_token( num, attr1, attr2, attr3 )
                if not tonumber( num ) or num < 0 then error( "Invalid or missing argument, expected number." ) end

                local len = math.min( tonumber( attr2 ) or 0, 56 ) or 0 -- limit to 56-bits
                local has_pad = attr1 == "0"
                local has_rev = attr1 == "-"
                local div = tonumber( attr3 ) or 0
                local res = ""

                local idx = 0
                while num > 0 or idx < len do
                        local rem = num % 2

                        -- prepend digits, since working from least to most significant bit
                        if div > 0 and idx % div == 0 then
                                res = " " .. res                                -- insert space between sets
                        end
                        if rem == 1 then
                                res = "1" .. res                                -- insert digit 1
                        elseif has_pad or num > 0 then
                                res = "0" .. res                                -- else, insert digit 0 or insert zero padding
                        else
                                res = has_rev and res .. " " or " " .. res      -- else, insert space padding
                        end

                        num = ( num - rem ) / 2
                        idx = idx + 1
                end

                return res
        end

        str = string.gsub( str, "(%%(.-)([A-Za-z]))", function ( token, attrs, option )
                if option == "b" and string.is_match( attrs, "^([0-]?)([0-9]*)$" ) or string.is_match( attrs, "^([0-]?)([0-9]*):([0-9]+)$" ) then
                        return parse_token( remove( args, 1 ), _[ 1 ], _[ 2 ], _[ 3 ] )
                else
                        return string.format( token, remove( args, 1 ) )
                end
        end )

        return str
end


Here are three examples of different inputs and the results, including pass-through string.format() specifiers:

Code: Select all
SOURCE
local num1 = 0xFF00
local num2 = 512
local num3 = 65000

print( bits_format( "The number %d in binary is %b.", num1, num1 ) )
print( bits_format( "dec = %d, bin = %016b, hex = %04x", num2, num2, num2 ) )
print( bits_format( "%032:4b", num3 ) )

OUTPUT
The number 65280 in binary is 1111111100000000.
dec = 512, bin = 0000001000000000, hex = 0200
0000 0000 0000 0000 1111 1101 1110 1000
 

User avatar
sorcerykid
Member
 
Posts: 1172
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Thu Jan 23, 2020 15:44

This is a command-line script to locate specific items that are placed or stored anywhere in the world. It is particularly helpful for server operators that are planning to uninstall a mod, but first want to determine how many items from that mod are actually in-use.

At the head of the script are six variables that will need to be set accordingly:
  • source_path = full path of the map.sqlite database to be opened in read-only mode
  • search_area = the mapblock boundaries in which to limit the search (set to nil to search the entire map)
  • search_items = hash of nodes, tools, or craftitems to find (each element must be set to 0, as these are counters)
  • is_placed = whether to check for search items placed as a nodes
  • is_stored = whether to check for search items stored in node inventories
  • containers = set of inventory nodes to include in search
The script is optimized so it will automatically skip mapblocks in which containers (in the case of storage search) or search_items (in the case of placement search) are not immediately found in the content ID lookup table. This saves valuable CPU cycles by only parsing and caching the nodemeta_map and node_list as necessary.

Code: Select all
require( "maplib" )

local source_path = "/home/minetest/.minetest/worlds/world/map.sqlite"
local search_area = MapArea( { x = -100, y = -5, z = -100 }, { x = 100, y = 2, z = 100 } )
local search_items = {
   ["default:pick_diamond"] = 0,
   ["default:mese"] = 0,
   ["default:goldblock"] = 0,
   ["default:steelblock"] = 0,
   ["default:steel_ingot"] = 0,
   ["default:gold_ingot"] = 0,
   ["nyancat:nyancat"] = 0,   
}
local is_placed = false
local is_stored = true
local containers = {
   ["default:chest"] = true,
   ["default:chest_locked"] = true,
}

---------------------------------------

local function has_containers( block )
   if is_stored then
      for i, n in pairs( block.nodename_map ) do
         if containers[ n ] then
            return true
         end
      end
   end
   return false
end

local function has_search_items( block )
   if is_placed then
      for i, n in pairs( block.nodename_map ) do
         if search_items[ n ] then
            return true   -- there is at least one, so confirm!
         end
      end
   end
   return false
end

local function stored_items_search( block, index, node_list, nodename_map )
   local is_found = false
   local nodemeta_map = block.get_nodemeta_map( )

        for pos, meta in pairs( nodemeta_map ) do
           local node_name = nodename_map[ node_list[ pos ].id ]

      if containers[ node_name ] then
         for idx, slot in ipairs( meta.inventory ) do
            local item_name = slot.item_name
            local item_count = slot.item_count

            if search_items[ item_name ] then
               is_found = true
               search_items[ item_name ] = search_items[ item_name ] + item_count

               if meta.fields.owner then
                  print( string.format( "[%d] Found %d of item '%s' stored in slot %d of %s owned by %s at %s.",
                     index, item_count, item_name, idx, node_name, meta.fields.owner, pos_to_string( decode_node_pos( pos, index ) )
                  ) )
               else
                  print( string.format( "[%d] Found %d of item '%s' stored in slot %d of %s at %s.",
                     index, item_count, item_name, idx, node_name, pos_to_string( decode_node_pos( pos, index ) )
                  ) )
               end
            end
         end
      end
   end

   return is_found
end

local function placed_items_search( block, index, node_list, nodename_map )
   local is_found = false

   for pos, node in ipairs( node_list ) do
      local node_name = nodename_map[ node.id ]

      if search_items[ node_name ] then
         is_found = true
         search_items[ node_name ] = search_items[ node_name ] + 1

         print( string.format( "[%d] Found item '%s' placed at %s.",
            index, node_name, pos_to_string( decode_node_pos( pos, index ) )
         ) )
      end
   end

   return is_found
end

-----------------------------------------

local map_db = MapDatabase( source_path, false )

if search_area then
   print( "Creating cache..." )
   map_db.create_cache( false )
end

print( "Examining database..." )

local iterator = search_area and map_db.iterate_area( search_area ) or map_db.iterate( )
local mapblock_count = 0
local mapblock_total = 0

if not is_stored and not is_placed then
   error( "Nothing to search for, aborting." )
end

for index, block in iterator do
   local has_containers = has_containers( block )
   local has_search_items = has_search_items( block )

   if has_containers or has_search_items then
      local node_list = block.get_node_list( )
      local nodename_map = block.nodename_map
      local is_found = false

      if is_stored and has_containers then
         is_found = stored_items_search( block, index, node_list, nodename_map )
      end
      if is_placed and has_search_items then
         is_found = placed_items_search( block, index, node_list, nodename_map )
      end

      if is_found then
         mapblock_count = mapblock_count + 1
      end
   end
   mapblock_total = mapblock_total + 1
end

print( string.format( "%d of %d mapblocks have %s search items.",
   mapblock_count,
   mapblock_total,
   not is_stored and "placed" or not is_placed and "stored" or "placed and stored"
) )

for i, v in pairs( search_items ) do
   print( string.format( "Found %d of item '%s'.", v, i ) )
end


This is an example output from a dummy map where I placed several rare items in various chests scattered around the spawnpoint. As you can see, it outputs the mapblock index in brackets, as well as the container name, position, slot, and owner.

Code: Select all
Creating cache...
Examining database...
[16777215] Found 1 of item 'nyancat:nyancat' stored in slot 1 of default:chest_locked owned by sorcerykid at (-2,4,16).
[33554432] Found 1 of item 'default:pick_diamond' stored in slot 2 of default:chest_locked owned by sorcerykid at (5,4,33).
[33554432] Found 22 of item 'default:goldblock' stored in slot 4 of default:chest_locked owned by sorcerykid at (5,4,33).
[33554432] Found 55 of item 'default:steel_ingot' stored in slot 30 of default:chest_locked owned by sorcerykid at (5,4,33).
[50331650] Found 99 of item 'default:mese' stored in slot 1 of default:chest at (38,4,54).
3 of 1682 mapblocks have stored search items.
Found 1 of item 'default:pick_diamond'.
Found 0 of item 'default:gold_ingot'.
Found 1 of item 'nyancat:nyancat'.
Found 99 of item 'default:mese'.
Found 55 of item 'default:steel_ingot'.
Found 0 of item 'default:steelblock'.
Found 22 of item 'default:goldblock'.


On my server, this script took less than 0.5 seconds to execute, even though it had to search 1682 mapblocks. Not too shabby :)
 

User avatar
sorcerykid
Member
 
Posts: 1172
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Sat Feb 01, 2020 10:22

Here's a new function for conditional pattern matching. But this time the results are stored in a dedicated comparison object. To construct the comparison object, simply call string.compare( ) with the string to be validated:

    cmp = string.compare( str )
The comparison object can then be used within any conditional statement to match against a pattern. The member functions is_match( ) or is_match_glob( ), return true if the pattern is a match:

    count = cmp.is_match( pattern )

    count = cmp.is_match_glob( pattern )
The is_match_glob( ) method is identical to is_match( ), but it evaluates a DOS-style glob rather than a typical Lua pattern. Afterward, it is then simply a matter of indexing the comparison object as an array to obtain the captures.

    cap_val = cmp[ cap_idx ]
Here is the code for the string.compare( ) constructor:

Code: Select all
function string.compare( str )
        local sanitizer =
        {
                ["["] = "";
                ["]"] = "";
                ["^"] = "%^";
                ["$"] = "%$";
                ["%"] = "%%";
                ["."] = "%.";
                ["-"] = "%-";
                ["*"] = "[A-Z0-9_-]*";
                ["+"] = "[A-Z0-9_-]+";
                ["?"] = "[A-Z0-9_-]";
                ["#"] = "%d";
                ["~"] = "%a";
        }
        local match = string.match
        local gsub = string.gsub
        local res
        local self = { }

        self.is_match = function ( pattern )
                res = { match( str, pattern ) }
                return #res > 0
        end
        self.is_match_glob = function ( glob )
                local pattern = "^" .. gsub( glob, ".", sanitizer ) .. "$"
                res = { match( str, pattern ) }
                return #res > 0
        end

        return setmetatable( self,
                __index = function ( k, v ) return res[ v ] end )
end


And this is an example of how it can be used in a scenario where you want to accept input either in the form of [distance] [axis] or else [x] [y] [z] where numbers can be decimals or floating points.

Code: Select all
local input = "3 X"
local cmp = string.compare( input )

local function move_by( a, b )
        print( a, b )
end

local function move_to( a, b, c )
        print( a, b, c )
end

if cmp.is_match( "(-?%d) ([XxYyZz])" ) or cmp.is_match"(-?%d%.%d) ([XxYyZz])" ) then
        move_by( string.lower( cmp[ 2 ] ), tonumber( cmp[ 1 ] ) )

elseif cmp.is_match( "(-?%d+)[, ](-?%d+)[, ](-?%d+)" ) or
                cmp.is_match( "(-?%d%.%d+)[, ](-?%d%.%d+)[, ](-?%d%.%d+)" ) then
        move_to( tonumber( cmp[ 1 ] ), tonumber( cmp[ 2 ] ), tonumber( cmp[ 3 ] ) )
else
        print( "Invalid input!" )
end
 

User avatar
sorcerykid
Member
 
Posts: 1172
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Fri Feb 07, 2020 14:00

While working on my cryptography mod last weekend, I came up with the following two helper functions that can be used to perform a logical bitwise NOT on either a WORD or DWORD value.

Code: Select all
function NOT16( num )
        return XOR( num, 0xFFFF )       -- 16-bit logical NOT
end

function NOT32( num )
        return XOR( num, 0xFFFFFFFF )   -- 32-bit logical NOT
end


In both cases, num is an unsigned integer of the appropriate size. If in doubt, then be sure to mask the input using the AND() function, otherwise the results will be undefined.
 

User avatar
v-rob
Member
 
Posts: 713
Joined: Thu Mar 24, 2016 03:19
Location: Right behind you.
GitHub: v-rob

Re: Post your code!

by v-rob » Sat Feb 08, 2020 20:37

Doesn't Lua have built-in bitwise operators?
 

User avatar
joe7575
Member
 
Posts: 341
Joined: Mon Apr 24, 2017 20:38
Location: Germany, in the deep south
GitHub: joe7575
In-game: JoSto

Re: Post your code!

by joe7575 » Sun Feb 09, 2020 11:07

v-rob wrote:Doesn't Lua have built-in bitwise operators?

Lua v5.3 has, Lua v5.1 not. Unfortunately, Minetest uses v5.1
Sent from my Commodore 64. My Mods: TechPack, Hyperloop, Tower Crane, Lumberjack, Iron Age, Entrance, Minecart, Signs Bot.
 

User avatar
Hybrid Dog
Member
 
Posts: 2726
Joined: Thu Nov 01, 2012 12:46

Re: Post your code!

by Hybrid Dog » Mon Feb 10, 2020 08:50

joe7575 wrote:
v-rob wrote:Doesn't Lua have built-in bitwise operators?

Lua v5.3 has, Lua v5.1 not. Unfortunately, Minetest uses v5.1

Luajit has bit operators, too. http://luajit.org/extensions.html

‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪
 

User avatar
sorcerykid
Member
 
Posts: 1172
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Fri Feb 14, 2020 19:03

This is the auth ruleset that I am currently using on the JT2 server. I find that it covers most general purpose needs.

  • denies anyone from joining with a deceptive name (admin, owner, staff, etc.)
  • denies anyone from joining as the server operator from a foreign IP address
  • blocks anyone that is repeatedly logging into the server (20 second timeout)
  • blocks anyone that failed to login multiple times in a row (60 second timeout)
  • blocks anyone from creating an account after a failed login (5 minute timeout)
  • denies anyone from creating an account when the server is over 50% capacity
  • denies any non-moderators from joining when the server is over 90% capacity

Code: Select all
#################################################################
#
# deny players with a reserved or restricted name
#
#################################################################

try "You do not have permission to join this server."

fail any
if $name in @blacklist.txt
if lc($name) is /*guest*/
if lc($name) is /*admin*/
continue

fail all
if $name in ("sorcerykid","Nemo","publicworks","citymanager","abba","justtest")
unless $addr is /127.?.?.?/a
continue

#################################################################
#
# deny players that have been inactive for one year
#
#################################################################

try "Sorry, this account has been disabled for inactivity."

fail all
if $is_new eq $false
if age($newlogin) gt 1y
continue

#################################################################
#
# deny new players when the server is nearing capacity
#
#################################################################

fail all
if $cur_users gt mul(0.5,$max_users)
unless $is_new eq $false
continue

fail all
if $cur_users gt mul(0.9,$max_users)
unless "basic_privs" in $privs_list
continue

#################################################################
#
# block new players after a recent failed login
#
#################################################################

try "You cannot create an account right now. Please try later."
fail all
if $is_new eq $true
if $ip_failures gt 0
unless age($ip_prelogin) gt 5m
continue

#################################################################
#
# block players that are spam-logging the server
#
#################################################################

try "This account is temporarily locked. Please try later."

fail all
if $is_new eq $false
if age($newlogin) lt 20s
continue

#################################################################
#
# block players after two or more failed logins
#
#################################################################

try "This account is temporarily locked. Please try later."

fail all
if $ip_attempts gt 0
if $ip_failures gte 2
if age($ip_newcheck) lt 60s
continue

pass now
 

Previous

Return to General Discussion



Who is online

Users browsing this forum: Google [Bot], WhoAreYou and 3 guests