Post your code!

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

Re: Post your code!

by Stix » Post

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: 1841
Joined: Fri Aug 26, 2016 15:36
GitHub: sorcerykid
In-game: Nemo
Location: Illinois, USA

Re: Post your code!

by sorcerykid » Post

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: 2828
Joined: Thu Nov 01, 2012 12:46
GitHub: HybridDog

Re: Post your code!

by Hybrid Dog » Post

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: 1841
Joined: Fri Aug 26, 2016 15:36
GitHub: sorcerykid
In-game: Nemo
Location: Illinois, USA

Re: Post your code!

by sorcerykid » Post

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: 851
Joined: Mon Apr 24, 2017 20:38
GitHub: joe7575
In-game: JoSto wuffi
Location: Germany, in the deep south

Re: Post your code!

by joe7575 » Post

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. Some of my Mods: Tech Age, TechPack, Hyperloop, Tower Crane, Lumberjack, vm16, Minecart, Signs Bot.

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

Re: Post your code!

by sorcerykid » Post

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: 845
Joined: Sun Jan 26, 2014 17:01
GitHub: gaelysam
IRC: Gael-de-Sailly
In-game: Gael-de-Sailly gaelysam
Location: Voiron, France

Re: Post your code!

by Gael de Sailly » Post

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: 1841
Joined: Fri Aug 26, 2016 15:36
GitHub: sorcerykid
In-game: Nemo
Location: Illinois, USA

Re: Post your code!

by sorcerykid » Post

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: 1841
Joined: Fri Aug 26, 2016 15:36
GitHub: sorcerykid
In-game: Nemo
Location: Illinois, USA

Re: Post your code!

by sorcerykid » Post

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: 1841
Joined: Fri Aug 26, 2016 15:36
GitHub: sorcerykid
In-game: Nemo
Location: Illinois, USA

Re: Post your code!

by sorcerykid » Post

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: 958
Joined: Wed Jun 24, 2015 18:45
GitHub: orwell96
IRC: orwell96_mt
In-game: orwell
Location: Raxacoricofallapatorius

Re: Post your code!

by orwell » Post

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: 2828
Joined: Thu Nov 01, 2012 12:46
GitHub: HybridDog

Re: Post your code!

by Hybrid Dog » Post

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: 1841
Joined: Fri Aug 26, 2016 15:36
GitHub: sorcerykid
In-game: Nemo
Location: Illinois, USA

Re: Post your code!

by sorcerykid » Post

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: 1841
Joined: Fri Aug 26, 2016 15:36
GitHub: sorcerykid
In-game: Nemo
Location: Illinois, USA

Re: Post your code!

by sorcerykid » Post

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: 1841
Joined: Fri Aug 26, 2016 15:36
GitHub: sorcerykid
In-game: Nemo
Location: Illinois, USA

Re: Post your code!

by sorcerykid » Post

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: 1841
Joined: Fri Aug 26, 2016 15:36
GitHub: sorcerykid
In-game: Nemo
Location: Illinois, USA

Re: Post your code!

by sorcerykid » Post

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
Developer
Posts: 970
Joined: Thu Mar 24, 2016 03:19
GitHub: v-rob
IRC: v-rob
Location: Right behind you.

Re: Post your code!

by v-rob » Post

Doesn't Lua have built-in bitwise operators?
Core Developer | My Best Mods: Bridger - Slats - Stained Glass

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

Re: Post your code!

by joe7575 » Post

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. Some of my Mods: Tech Age, TechPack, Hyperloop, Tower Crane, Lumberjack, vm16, Minecart, Signs Bot.

User avatar
Hybrid Dog
Member
Posts: 2828
Joined: Thu Nov 01, 2012 12:46
GitHub: HybridDog

Re: Post your code!

by Hybrid Dog » Post

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: 1841
Joined: Fri Aug 26, 2016 15:36
GitHub: sorcerykid
In-game: Nemo
Location: Illinois, USA

Re: Post your code!

by sorcerykid » Post

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

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

Re: Post your code!

by sorcerykid » Post

The following script plots a frequency histogram of mapblock sizes within your database, making it a quick and easy means of visualizing relative size distribution.

At the head of the script are six variables that can be changed to customize the output:
  • source_path = full path of the map.sqlite database to be opened in read-only mode
    interval = the width of each sample group in kilobytes
    low_cutoff = the lowest sample group to plot (optional)
    high_cutoff = the highest sample sample group to plot (optional)
    is_compressed = if true, then a log transform will be applied to reduce the effect of outliers
    is_continuous = if true, then all sample groups will be plotted even if they are zero
Image

Code: Select all

package.path = "/home/minetest/maplib/?.lua;" .. package.path
local maplib = require "maplib"

local source_path = "/home/minetest.minetest/worlds/test/map.sqlite"
local interval = 1
local low_cutoff = 1
local high_cutoff = nil
local screen_size = 80
local is_continuous = true
local is_compressed = true

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

local map_db = MapDatabase( source_path, true )

print( "Examining database..." )

local bins = { }
local max_length = 0
local min_length = 0
local max_count = 0

for index, block in map_db.iterate( ) do
        local class = math.ceil( block.length / 1024 / interval )
        local count = bins[ class ] or 0

        bins[ class ] = count + 1


        if count > max_count then
                max_count = count
        end

        if block.length > max_length then
                max_length = block.length
        end
        if min_length == 0 or block.length < min_length then
                min_length = block.length
        end
end

print( string.format( "Max mapblock size = %d bytes", max_length ) )
print( string.format( "Min mapblock size = %d bytes", min_length ) )

for idx = low_cutoff or 1, high_cutoff or math.ceil( max_length / 1024 / interval ) do
        local count = bins[ idx ] or 0

        if count > 0 or is_continuous then
                local bar_size = is_compressed and
                        math.ceil( math.log( count + 1 ) / math.log( max_count + 1 ) * screen_size ) or
                        math.ceil( count / max_count * screen_size )

                print( string.format( "%6.1f kB: %s (%d)", idx * interval, string.rep( "#", bar_size ), count ) )
        end
end

cHyper-0815OL

Re: Post your code!

by cHyper-0815OL » Post

? how can i enable torch lighting for mineclone2?

User avatar
Hybrid Dog
Member
Posts: 2828
Joined: Thu Nov 01, 2012 12:46
GitHub: HybridDog

Re: Post your code!

by Hybrid Dog » Post

Image

Code: Select all

---------------------- Config --------------------------------------------------
-- rmin has to be >= zinz * 4 - 2
local rmin = 23
local rmax = 39
local zinz = 3
local roomheight = 5


----------------------- Helpers ------------------------------------------------

local blocklen = 4 * zinz - 2
local set = minetest.set_node
local get = minetest.get_node
local poshash = minetest.hash_node_position

local function hash2(x, y)
	return y * 0x10000 + x
end

vector.from_number = vector.from_number or function(z)
	return {x=z, y=z, z=z}
end


------------------------- Build funcs ------------------------------------------

-- returns a perlin chunk field of positions
local default_nparams = {
   offset = 0,
   scale = 1,
   seed = 3337,
   octaves = 6,
   persist = 0.6
}
local function get_perlin_field(rmin, rmax, nparams)
	local r = math.ceil(rmax)
	nparams = nparams or {}
	for i,v in pairs(default_nparams) do
		nparams[i] = nparams[i] or v
	end
	nparams.spread = nparams.spread or vector.from_number(r*5)

	local pos = {x=math.random(-30000, 30000), y=math.random(-30000, 30000)}
	local map = minetest.get_perlin_map(
		nparams,
		vector.from_number(r+r+1)
	):get2dMap_flat(pos)

	local id = 1

	local bare_maxdist = rmax*rmax
	local bare_mindist = rmin*rmin

	local mindist = math.sqrt(bare_mindist)
	local dist_diff = math.sqrt(bare_maxdist)-mindist
	mindist = mindist/dist_diff

	local pval_min, pval_max

	local tab, n = {}, 1
	for z=-r,r do
		local bare_dist = z*z
		for x=-r,r do
			local bare_dist = bare_dist+x*x
			local add = bare_dist < bare_mindist
			local pval, distdiv
			if not add
			and bare_dist <= bare_maxdist then
				distdiv = math.sqrt(bare_dist)/dist_diff-mindist
				pval = math.abs(map[id]) -- fix values > 1
				if not pval_min then
					pval_min = pval
					pval_max = pval
				else
					pval_min = math.min(pval, pval_min)
					pval_max = math.max(pval, pval_max)
				end
				add = true--distdiv < 1-math.abs(map[id])
			end

			if add then
				tab[n] = {x,z, pval, distdiv}
				n = n+1
			end
			id = id+1
		end
	end

	-- change strange values
	local pval_diff = pval_max - pval_min
	pval_min = pval_min/pval_diff

	for n,i in pairs(tab) do
		if i[3] then
			local new_pval = math.abs(i[3]/pval_diff - pval_min)
			if i[4] < new_pval then
				tab[n] = {i[1], i[2]}
			else
				tab[n] = nil
			end
		end
	end
	return tab
end

-- tests whether enough space is available for the block to fit in
local function block_fits(x,z, xzarea_h)
	for z = z, z + blocklen-1 do
		for x = x, x + blocklen-1 do
			if not xzarea_h[hash2(x, z)] then
				return false
			end
		end
	end
	return true
end

-- returns the block xz corners relative to pos
local boffs = {{0, -blocklen}, {-blocklen, 0}, {blocklen, 0}, {0, blocklen}}
local function get_block_xzs(xzarea_h)
	local blocks = {}
	local todo = {{math.random(blocklen) - 2, math.random(blocklen) - 2}}
	local avoid = {}
	local sp = 1
	while sp > 0 do
		local x,z = todo[sp][1],todo[sp][2]
		sp = sp-1
		for i = 1,#boffs do
			local x = x + boffs[i][1]
			local z = z + boffs[i][2]
			local h = hash2(x, z)
			if not avoid[h] then
				avoid[h] = true
				if block_fits(x,z, xzarea_h) then
					local p = {x, z}
					sp = sp+1
					todo[sp] = p
					blocks[#blocks+1] = p
				end
			end
		end
	end
	return blocks
end

-- finds the minimum y top of a block
local function block_minh(pos, x,z)
	local leasth = pos.y + 10 - 50
	for z = z, z + blocklen-1 do
		for x = x, x + blocklen-1 do
			local h
			local ystart = pos.y + 10
			-- search up
			for y = math.max(ystart, leasth+1), ystart + 13 do
				if get{x=pos.x+x, y=pos.y+y, z=pos.z+z}.name == "air" then
					break
				end
				h = y
			end
			if h then
				leasth = h
			else
				-- search down
				for y = ystart-1, leasth+1, -1 do
					if get{x=pos.x+x, y=pos.y+y, z=pos.z+z}.name ~= "air" then
						leasth = y
						break
					end
				end
			end
		end
	end
	return leasth
end

-- gives top heights to blocks
local function get_least_heights(block_xzs, pos)
	local height_offset = math.random(roomheight)-1
	local least_heights = {}
	for i = 1,#block_xzs do
		local x,z = unpack(block_xzs[i])
		local h = block_minh(pos, x,z)
		h = math.ceil((h + height_offset) / roomheight) * roomheight
		least_heights[hash2(x,z)] = h
	end
	return least_heights
end

-- tests whether a block can be added to x,y,z
local function is_block(y, block_xzs_h, tops, bottom, h)
	return y >= bottom
		and block_xzs_h[h]
		and y <= tops[h]
end

-- makes blocks 3d
local boffs3d = {
	{0, 0, -blocklen}, {-blocklen, 0, 0}, {blocklen, 0, 0}, {0, 0, blocklen},
	{0, -roomheight, 0}, {0, roomheight, 0},
}
local function get_blocks_3d(block_xzs, block_xzs_h, tops, bottom)
	local blocks = {}
	local x,z = unpack(block_xzs[1])
	local y = bottom

	local todo = {{x,y,z}}
	local avoid = {}
	local sp = 1
	while sp > 0 do
		local prev_block = todo[sp]
		local x,y,z = unpack(prev_block)
		sp = sp-1
		for i = 1,#boffs3d do
			local x = x + boffs3d[i][1]
			local y = y + boffs3d[i][2]
			local z = z + boffs3d[i][3]
			local h = poshash{x=x, y=y, z=z}
			if not avoid[h] then
				avoid[h] = true
				local xzh = hash2(x, z)
				if is_block(y, block_xzs_h, tops, bottom, xzh) then
					local p = {x, y, z, prev_block, y == tops[xzh]}
					sp = sp+1
					todo[sp] = p
					blocks[#blocks+1] = p
				end
			end
		end
	end
	return blocks
end

-- gets the building's blocks with their connections
local function get_blocks(xzarea_h, pos)
	local block_xzs = get_block_xzs(xzarea_h)
	local tops = get_least_heights(block_xzs, pos)

	local lowest_top
	for _,h in pairs(tops) do
		lowest_top = lowest_top and math.min(lowest_top, h) or h
	end
	local bottom = lowest_top - 2 * roomheight

	local block_xzs_h = {}
	for i = 1,#block_xzs do
		block_xzs_h[hash2(block_xzs[i][1], block_xzs[i][2])] = true
	end
	return get_blocks_3d(block_xzs, block_xzs_h, tops, bottom)
end

-- builds it
local function set_blocks(pos, blocks)
	local connections = {}
	for i = 1,#blocks do
		local x,y,z, toconnect, top = unpack(blocks[i])
		if toconnect then
			connections[#connections+1] = {blocks[i], toconnect}
		end
		-- set walls
		for y = y, y + roomheight-1 do
			for i = -1,1,2 do
				for x = x, x + blocklen-1 do
					local p = {x=pos.x+x, y=y, z=pos.z+z}
					if i == 1 then
						p.z = p.z + blocklen-1
					end
					set(p, {name="default:cobble"})
				end
				for z = z, z + blocklen-1 do
					local p = {x=pos.x+x, y=y, z=pos.z+z}
					if i == 1 then
						p.x = p.x + blocklen-1
					end
					set(p, {name="default:cobble"})
				end
			end
		end
		if top then
			-- battlements
			local o = 0
			for _ = 1,zinz do
				for i = 0,1 do
					local p = {x=pos.x+x+o+i, y=y+roomheight, z=pos.z+z}
					set(p, {name="default:cobble"})
					p.z = p.z + blocklen-1
					set(p, {name="default:cobble"})

					p = {x=pos.x+x, y=y+roomheight, z=pos.z+z+o+i}
					set(p, {name="default:cobble"})
					p.x = p.x + blocklen-1
					set(p, {name="default:cobble"})
				end
				o = o + 4
			end
		end
		-- set floor
		for z = z+1, z + blocklen-2 do
			for x = x+1, x + blocklen-2 do
				local p = {x=pos.x+x, y=y, z=pos.z+z}
				set(p, {name="default:stone"})
			end
		end
		-- set ceiling
		for z = z+1, z + blocklen-2 do
			for x = x+1, x + blocklen-2 do
				local p = {x=pos.x+x, y=y + roomheight-1, z=pos.z+z}
				set(p, {name = top and "default:dirt" or "default:meselamp"})
			end
		end
		-- set room air
		for z = z+1, z + blocklen-2 do
			for y = y+1, y + roomheight-2 do
				for x = x+1, x + blocklen-2 do
					local p = {x=pos.x+x, y=y, z=pos.z+z}
					set(p, {name="air"})
				end
			end
		end
	end
	for i = 1,#connections do
		local x1,y1,z1 = unpack(connections[i][1])
		local x2,y2,z2 = unpack(connections[i][2])
		if x1 ~= x2 then
			-- hole in the xwalls
			local x = math.max(x1, x2)
			for yo = 1,2 do
				local p = {x=pos.x+x, y=y1+yo, z=pos.z+z1+blocklen/2}
				set(p, {name="air"})
				p.x = p.x-1
				set(p, {name="air"})
			end
		elseif z1 ~= z2 then
			-- hole in zwalls
			local z = math.max(z1, z2)
			for yo = 1,2 do
				local p = {z=pos.z+z, y=y1+yo, x=pos.x+x1+blocklen/2}
				set(p, {name="air"})
				p.z = p.z-1
				set(p, {name="air"})
			end
		else
			-- ladder for climbing to the other room
			local y = y1 > y2 and y2 or y1
			for y = y+1, y+1 + roomheight do
				local p = {z=pos.z+z1+1, y=y, x=pos.x+x1+1}
				set(p, {name="default:ladder", param2=5})
			end
		end
	end
end

-- makes the building
local function burgr(pos)
	local xzarea = get_perlin_field(rmin, rmax)
	local xzarea_h = {}
	for i,v in pairs(xzarea) do
		xzarea_h[hash2(v[1], v[2])] = i
	end
	local blocks = get_blocks(xzarea_h, pos)
	set_blocks(pos, blocks)
end


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

-- Testing code for this house generator
minetest.override_item("default:wood", {
	tiles = {"default_wood.png", "default_brick.png"},
	on_place = function(_,_, pt)
		burgr(pt.above)
	end,
})


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


--~ minetest.override_item("default:wood", {
	--~ after_place_node = function(pos)
		--~ for i = 1,1000 do
			--~ minetest.set_node(pos, {name="default:wood"})
			--~ minetest.remove_node(pos)
		--~ end
	--~ end
--~ })

--[[
-- Test code for node placement and dig predictions
minetest.override_item("default:wood", {
	tiles = {"default_wood.png", "default_brick.png"},
	node_placement_prediction = "default:stone",
	node_dig_prediction = "default:cobble",
	can_dig = function() return false end,
	--~ on_place = function(_,_, pt)
		--~ minetest.set_node(pt.under, {name="default:mese"})
	--~ end,
})
]]

--~ minetest.register_chatcommand("a", {
    --~ func = function(name)
        --~ minetest.show_formspec(name, "a", [[
            --~ size[4,4]
            --~ bgcolor[red]
            --~ label[1,1;hi]
        --~ ]])
    --~ end,
--~ })
Attachments
mytest.jpg
mytest.jpg (158.47 KiB) Viewed 4890 times

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

User avatar
AspireMint
Member
Posts: 415
Joined: Mon Jul 09, 2012 12:59
GitHub: AspireMint
IRC: AspireMint
In-game: AspireMint
Location: Stuck at spawn

Re: Post your code!

by AspireMint » Post

Try-catch block:

Code: Select all

Exception = ''
FileException = 'No such file or directory'
CustomException = '[0-9]'
-- add your own exceptions...

function try(fn)
    function TCBlock()
        local o = {
            status = nil,
            exception = nil
        }
        
        o.try = function(fn)
            o.status, o.exception = pcall(fn)
            o.try = nil
            return o
        end

        local finally = function(fn)
            fn()
        end
            
        o.catch = function(error_pattern, fn)
            if not o.status and string.match(o.exception, error_pattern) then
                fn(o.exception)
            end
            o.finally = finally
            return o
        end

        return o
    end
    
    return TCBlock().try(fn)
end
usage:

Code: Select all

try(function()
    dofile("./lua.lua")
    print("not in console because file does not exist")
end)
.catch(Exception, function(err)
    print("catch anything", err)
end)
.catch(FileException, function(err)
    print("catch file exception", err)
end)
.finally(function()
    print("that's all folks")
end)
EDIT: added type (string pattern) to catch only desired exceptions and bugfix

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

Re: Post your code!

by sorcerykid » Post

A player on my sever recently notified me that the screwdriver can be used to rotate chests sideways. So I wrote a command-line Lua script to locate all such incorrectly oriented chests, furnaces, bookshelves, and vessels shelves within the world.

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 (comment out to search the entire map)

Code: Select all

-- Find all chests, locked chests, bookshelves, vessels shelves, and
-- furnaces that are incorrectly oriented (i.e. param2 >= 4)

package.path = "/home/minetest/maplib/?.lua;" .. package.path

local maplib = require "maplib"

local source_path = "/home/minetest/.minetest/worlds/jt2/map.sqlite"
--local search_area = MapArea( { x = -10, y = 0, z = -10 }, { x = 10, y = 0, z = 10 } )

local containers = {
        ["default:furnace"] = true,
        ["default:chest"] = true,
        ["default:chest_locked"] = true,
        ["default:bookshelf"] = true,
        ["vessels:shelf"] = true,
--        ["vendor:vendor"] = true,
--        ["vendor:depositor"] = true,
}

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

local pos_to_string = maplib.pos_to_string
local decode_node_pos = maplib.decode_node_pos

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

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

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

                if containers[ node_name ] and node.param2 >= 4 then
                        print( string.format( "Found %s at %s", node_name, pos_to_string( pos ) ) )
                end
        end
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( )

for index, block in iterator do
        if has_containers( block ) then
                local node_list = block.get_node_list( )
                local nodename_map = block.nodename_map

                analyze_block( block, index, node_list, nodename_map )
        end
end

Post Reply

Who is online

Users browsing this forum: No registered users and 8 guests