forked from silverwizard/CharGen
parent
ba941d6f47
commit
22da63b4bc
@ -0,0 +1,11 @@ |
|||||||
|
CC=cc
|
||||||
|
LIBS=-lm
|
||||||
|
|
||||||
|
generator: |
||||||
|
${CC} -o charactergenerator ${LIBS} character.c
|
||||||
|
server: |
||||||
|
${CC} -o chargen chargen.c
|
||||||
|
|
||||||
|
all: server generator |
||||||
|
|
||||||
|
.PHONY: all |
@ -0,0 +1,437 @@ |
|||||||
|
#include <string.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <math.h> |
||||||
|
#include "character.h" |
||||||
|
|
||||||
|
int dieroll(int num,int sides){ |
||||||
|
int total = 0; |
||||||
|
for(num;num>0;num--){ |
||||||
|
total = total + arc4random_uniform(sides)+1; |
||||||
|
} |
||||||
|
return total; |
||||||
|
} |
||||||
|
|
||||||
|
void addLanguage(long lang){ |
||||||
|
if((lang & languages ) == 0){ |
||||||
|
languages = languages + lang; |
||||||
|
}else{ |
||||||
|
addLanguage(exp2l(arc4random_uniform(15))); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void addProf(long prof){ |
||||||
|
if((prof & profs) == 0){ |
||||||
|
profs = profs + prof; |
||||||
|
}else{ |
||||||
|
addProf(exp2l(arc4random_uniform(18))); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void classProfs(unsigned long* classprofs, int count, int num){ |
||||||
|
//Remove existing profs from the array
|
||||||
|
for(int i=0; i<=count;i++){ |
||||||
|
if((classprofs[i] & profs) != 0){ |
||||||
|
classprofs[i] = classprofs[count]; |
||||||
|
count--; |
||||||
|
i = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
//Shuffle the array
|
||||||
|
for(int i=0; i<=count;i++){ |
||||||
|
int newpos = i + arc4random_uniform(count - i); |
||||||
|
unsigned long local = classprofs[newpos]; |
||||||
|
classprofs[newpos] = classprofs[i]; |
||||||
|
classprofs[i]=local; |
||||||
|
} |
||||||
|
//Grab the first N elements, based on the class's value, but first, if we have more profs than available profs, get some random ones
|
||||||
|
if(num > count){ |
||||||
|
for(count;num > count;count--){ |
||||||
|
addProf(exp2l(arc4random_uniform(18))); |
||||||
|
} |
||||||
|
} |
||||||
|
for(int i=0;i<=num;i++){ |
||||||
|
addProf(classprofs[num]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void genClass(){ |
||||||
|
int c = arc4random_uniform(sizeof(classes)/sizeof(classes[0])); |
||||||
|
class = classes[c]; |
||||||
|
switch(c){ |
||||||
|
case BARBARIAN:; |
||||||
|
unsigned long barbskills[6] = {ANIMALHANDLING, ATHLETICS, INTIMIDATION, NATURE, PERCEPTION, SURVIVAL}; |
||||||
|
hp = 12 + mods[CON]; |
||||||
|
classProfs(barbskills, 6, 2); |
||||||
|
break; |
||||||
|
case BARD:; |
||||||
|
addProf(exp2l(arc4random_uniform(18))); |
||||||
|
addProf(exp2l(arc4random_uniform(18))); |
||||||
|
addProf(exp2l(arc4random_uniform(18))); |
||||||
|
strlcat(otherprofs, "Three Musical Instruments, ", sizeof(otherprofs)); |
||||||
|
hp = 8 + mods[CON]; |
||||||
|
break; |
||||||
|
case CLERIC:; |
||||||
|
unsigned long clericskills[5]= {HISTORY, INSIGHT, MEDICINE, PERSUASION, RELIGION}; |
||||||
|
classProfs(clericskills, 5, 2); |
||||||
|
hp = 8 + mods[CON]; |
||||||
|
break; |
||||||
|
case DRUID:; |
||||||
|
unsigned long druidskills[8] = {ARCANA, ANIMALHANDLING, INSIGHT, MEDICINE, NATURE, PERCEPTION, RELIGION, SURVIVAL}; |
||||||
|
classProfs(druidskills, 8, 2); |
||||||
|
strlcat(otherprofs, "Herbalism Kit, ", sizeof(otherprofs)); |
||||||
|
hp = 8 + mods[CON]; |
||||||
|
break; |
||||||
|
case FIGHTER:; |
||||||
|
unsigned long fighterskills[8] = {ACROBATICS, ANIMALHANDLING, ATHLETICS, HISTORY, INSIGHT, INTIMIDATION, PERCEPTION, SURVIVAL}; |
||||||
|
classProfs(fighterskills, 8, 2); |
||||||
|
hp = 10 + mods[CON]; |
||||||
|
break; |
||||||
|
case MONK:; |
||||||
|
unsigned long monkskills[6] = {ACROBATICS, ATHLETICS, HISTORY, INSIGHT, RELIGION, STEALTH}; |
||||||
|
classProfs(monkskills, 6, 2); |
||||||
|
strlcat(otherprofs, "Artisan Tool or Musical Instrument, ", sizeof(otherprofs)); |
||||||
|
hp = 8 + mods[CON]; |
||||||
|
break; |
||||||
|
case PALADIN:; |
||||||
|
unsigned long paladinskills[6] = {ATHLETICS, INSIGHT, INTIMIDATION, MEDICINE, PERSUASION, RELIGION}; |
||||||
|
classProfs(paladinskills, 6, 2); |
||||||
|
hp = 10 + mods[CON]; |
||||||
|
break; |
||||||
|
case RANGER:; |
||||||
|
unsigned long rangerskills[8] = {ANIMALHANDLING, ATHLETICS, INSIGHT, INVESTIGATION, NATURE, PERCEPTION, STEALTH, SURVIVAL}; |
||||||
|
classProfs(rangerskills, 8, 3); |
||||||
|
hp = 10 + mods[CON]; |
||||||
|
break; |
||||||
|
case ROGUE:; |
||||||
|
unsigned long rogueskills[11] = {ACROBATICS, ATHLETICS, DECEPTION, INSIGHT, INTIMIDATION, INVESTIGATION, PERCEPTION, PERFORMANCE, PERSUASION, SLEIGHTOFHAND, STEALTH}; |
||||||
|
classProfs(rogueskills, 11, 4); |
||||||
|
strlcat(otherprofs, "Thieves tools, ", sizeof(otherprofs)); |
||||||
|
hp = 8 + mods[CON]; |
||||||
|
break; |
||||||
|
case SORCERER:; |
||||||
|
unsigned long sorcererskills[6] = {ARCANA, DECEPTION, INSIGHT, INTIMIDATION, PERSUASION, RELIGION}; |
||||||
|
classProfs(sorcererskills, 6, 2); |
||||||
|
hp = 6 + mods[CON]; |
||||||
|
break; |
||||||
|
case WARLOCK:; |
||||||
|
unsigned long warlockskills[7] = {ARCANA, DECEPTION, HISTORY, INTIMIDATION, INVESTIGATION, NATURE, RELIGION}; |
||||||
|
classProfs(warlockskills, 7, 2); |
||||||
|
hp = 8 + mods[CON]; |
||||||
|
break; |
||||||
|
case WIZARD:; |
||||||
|
unsigned long wizardskills[5] = {ARCANA, HISTORY, INVESTIGATION, MEDICINE, RELIGION}; |
||||||
|
classProfs(wizardskills, 5, 2); |
||||||
|
hp = 6 + mods[CON]; |
||||||
|
break; |
||||||
|
default: |
||||||
|
fprintf(stderr, "Incorrect class was generated for some reason"); |
||||||
|
/* This should never happen */ |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void genRace(){ |
||||||
|
int r = arc4random_uniform(sizeof(races)/sizeof(races[0])); |
||||||
|
race = races[r]; |
||||||
|
int subrace; |
||||||
|
switch (r){ |
||||||
|
case DRAGONBORN: |
||||||
|
addLanguage(DRACONIC); |
||||||
|
stats[STR]+=2; |
||||||
|
stats[CHA]+=1; |
||||||
|
break; |
||||||
|
case DWARF: |
||||||
|
addLanguage(DWARVISH); |
||||||
|
stats[CON]+=2; |
||||||
|
subrace = arc4random_uniform(2); |
||||||
|
if(subrace == 0){ |
||||||
|
race = "Hill Dwarf"; |
||||||
|
stats[WIS]+=1; |
||||||
|
}else{ |
||||||
|
race = "Mountain Dwarf"; |
||||||
|
stats[STR]+=2; |
||||||
|
} |
||||||
|
break; |
||||||
|
case ELF: |
||||||
|
addLanguage(ELVISH); |
||||||
|
addProf(PERCEPTION); |
||||||
|
stats[DEX]+=2; |
||||||
|
subrace = arc4random_uniform(3); |
||||||
|
if(subrace == 0){ |
||||||
|
race = "High Elf"; |
||||||
|
stats[INT]+=1; |
||||||
|
addLanguage(exp2l(arc4random_uniform(15))); |
||||||
|
}else if(subrace == 1){ |
||||||
|
race = "Wood Elf"; |
||||||
|
stats[WIS]+=1; |
||||||
|
}else{ |
||||||
|
race = "Drow"; |
||||||
|
stats[CHA]+=1; |
||||||
|
} |
||||||
|
break; |
||||||
|
case GNOME: |
||||||
|
subrace = arc4random_uniform(2); |
||||||
|
stats[INT]+=2; |
||||||
|
addLanguage(GNOMISH); |
||||||
|
if(subrace == 0){ |
||||||
|
race = "Forest Gnome"; |
||||||
|
stats[DEX]+=1; |
||||||
|
}else{ |
||||||
|
race = "Rock Gnome"; |
||||||
|
stats[CON]+=1; |
||||||
|
strlcat(otherprofs, "Artisan Tools, ", sizeof(otherprofs)); |
||||||
|
} |
||||||
|
break; |
||||||
|
case HALFELF: |
||||||
|
stats[CHA]+=2; |
||||||
|
stats[arc4random_uniform(4)]+=1; |
||||||
|
stats[arc4random_uniform(4)]+=1; |
||||||
|
addProf(exp2l(arc4random_uniform(18))); |
||||||
|
addProf(exp2l(arc4random_uniform(18))); |
||||||
|
addLanguage(exp2l(arc4random_uniform(15))); |
||||||
|
addLanguage(ELVISH); |
||||||
|
break; |
||||||
|
case HALFLING: |
||||||
|
addLanguage(HALFLISH); |
||||||
|
stats[DEX]+=2; |
||||||
|
subrace = arc4random_uniform(2); |
||||||
|
if(subrace == 0){ |
||||||
|
race = "Lightfoot Halfling"; |
||||||
|
stats[CHA]+=1; |
||||||
|
}else{ |
||||||
|
race = "Stout Halfling"; |
||||||
|
stats[INT]+=1; |
||||||
|
} |
||||||
|
break; |
||||||
|
case HALFORC: |
||||||
|
addLanguage(ORC); |
||||||
|
stats[STR]+=2; |
||||||
|
stats[CON]+=1; |
||||||
|
addProf(INTIMIDATION); |
||||||
|
break; |
||||||
|
case HUMAN: |
||||||
|
stats[STR]+=1; |
||||||
|
stats[DEX]+=1; |
||||||
|
stats[CON]+=1; |
||||||
|
stats[INT]+=1; |
||||||
|
stats[WIS]+=1; |
||||||
|
stats[CHA]+=1; |
||||||
|
addLanguage(exp2l(arc4random_uniform(15))); |
||||||
|
break; |
||||||
|
case TIEFLING: |
||||||
|
stats[INT]+=1; |
||||||
|
stats[CHA]+=2; |
||||||
|
addLanguage(INFERNAL); |
||||||
|
break; |
||||||
|
default: |
||||||
|
fprintf(stderr, "Something went wrong in race selection"); |
||||||
|
/*Hopefully should never happen*/ |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void genBackground(){ |
||||||
|
int b = arc4random_uniform(sizeof(backgrounds)/sizeof(backgrounds[0])); |
||||||
|
background = backgrounds[b]; |
||||||
|
switch (b){ |
||||||
|
case ACOLYTE: |
||||||
|
addProf(INSIGHT); |
||||||
|
addProf(RELIGION); |
||||||
|
addLanguage(exp2l(arc4random_uniform(15))); |
||||||
|
addLanguage(exp2l(arc4random_uniform(15))); |
||||||
|
break; |
||||||
|
case CHARLATAN: |
||||||
|
addProf(DECEPTION); |
||||||
|
addProf(SLEIGHTOFHAND); |
||||||
|
strlcat(otherprofs, "Disguise Kit, ", sizeof(otherprofs)); |
||||||
|
strlcat(otherprofs, "Forgery Kit, ", sizeof(otherprofs)); |
||||||
|
break; |
||||||
|
case CRIMINAL: |
||||||
|
addProf(DECEPTION); |
||||||
|
addProf(STEALTH); |
||||||
|
strlcat(otherprofs, "Gaming Set, ", sizeof(otherprofs)); |
||||||
|
strlcat(otherprofs, "Theives Tools, ", sizeof(otherprofs)); |
||||||
|
break; |
||||||
|
case ENTERTAINER: |
||||||
|
addProf(ACROBATICS); |
||||||
|
addProf(PERFORMANCE); |
||||||
|
strlcat(otherprofs, "Disguise Kit, ", sizeof(otherprofs)); |
||||||
|
strlcat(otherprofs, "Musical Instrument, ", sizeof(otherprofs)); |
||||||
|
break; |
||||||
|
case FOLKHERO: |
||||||
|
addProf(ANIMALHANDLING); |
||||||
|
addProf(SURVIVAL); |
||||||
|
strlcat(otherprofs, "Artisan's Tools, ", sizeof(otherprofs)); |
||||||
|
strlcat(otherprofs, "Land Vehicles, ", sizeof(otherprofs)); |
||||||
|
break; |
||||||
|
case GUILDARTISAN: |
||||||
|
addProf(INSIGHT); |
||||||
|
addProf(PERSUASION); |
||||||
|
addLanguage(exp2l(arc4random_uniform(15))); |
||||||
|
strlcat(otherprofs, "Artisan's Tools, ", sizeof(otherprofs)); |
||||||
|
break; |
||||||
|
case HERMIT: |
||||||
|
addProf(MEDICINE); |
||||||
|
addProf(RELIGION); |
||||||
|
addLanguage(exp2l(arc4random_uniform(15))); |
||||||
|
strlcat(otherprofs, "Herbalism Kit, ", sizeof(otherprofs)); |
||||||
|
break; |
||||||
|
case NOBLE: |
||||||
|
addProf(HISTORY); |
||||||
|
addProf(PERSUASION); |
||||||
|
addLanguage(exp2l(arc4random_uniform(15))); |
||||||
|
strlcat(otherprofs, "Gaming Set, ", sizeof(otherprofs)); |
||||||
|
break; |
||||||
|
case OUTLANDER: |
||||||
|
addProf(ATHLETICS); |
||||||
|
addProf(SURVIVAL); |
||||||
|
addLanguage(exp2l(arc4random_uniform(15))); |
||||||
|
strlcat(otherprofs, "Musical Instrument, ", sizeof(otherprofs)); |
||||||
|
break; |
||||||
|
case SAGE: |
||||||
|
addProf(ARCANA); |
||||||
|
addProf(HISTORY); |
||||||
|
addLanguage(exp2l(arc4random_uniform(15))); |
||||||
|
addLanguage(exp2l(arc4random_uniform(15))); |
||||||
|
break; |
||||||
|
case SAILOR: |
||||||
|
addProf(ATHLETICS); |
||||||
|
addProf(PERCEPTION); |
||||||
|
strlcat(otherprofs, "Navigator's Tools, ", sizeof(otherprofs)); |
||||||
|
strlcat(otherprofs, "Water Vehicles, ", sizeof(otherprofs)); |
||||||
|
break; |
||||||
|
case SOLDIER: |
||||||
|
addProf(ATHLETICS); |
||||||
|
addProf(INTIMIDATION); |
||||||
|
strlcat(otherprofs, "Gaming Set, ", sizeof(otherprofs)); |
||||||
|
strlcat(otherprofs, "Land Vehicles, ", sizeof(otherprofs)); |
||||||
|
break; |
||||||
|
case URCHIN: |
||||||
|
addProf(SLEIGHTOFHAND); |
||||||
|
addProf(STEALTH); |
||||||
|
strlcat(otherprofs, "Theives Tools, ", sizeof(otherprofs)); |
||||||
|
strlcat(otherprofs, "Disguise Kit, ", sizeof(otherprofs)); |
||||||
|
break; |
||||||
|
default: |
||||||
|
fprintf(stderr, "Bad background was generated!"); |
||||||
|
/* Ideally not reached */ |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int main(){ |
||||||
|
int i; |
||||||
|
strlcat(otherprofs, "", 1); |
||||||
|
genRace(); |
||||||
|
genClass(); |
||||||
|
genBackground(); |
||||||
|
for(i=0; i < 6; i++){ |
||||||
|
stats[i] = dieroll(3,6); |
||||||
|
mods[i] = floor((stats[i]/2)-5); |
||||||
|
} |
||||||
|
printf("Race = %s\nBackground = %s\nClass = %s\nStr = %d %d\nDex = %d %d\nCon = %d %d\nInt = %d %d\nWis = %d %d\nCha = %d %d\n\nHP: %d\n", race, background, class, stats[STR], mods[STR], stats[DEX], mods[DEX], stats[CON], mods[CON], stats[INT], mods[INT], stats[WIS], mods[WIS], stats[CHA], mods[CHA], hp); |
||||||
|
|
||||||
|
|
||||||
|
printf("Proficiences:\n"); |
||||||
|
if((ATHLETICS & profs) != 0){ |
||||||
|
printf("Athletics\n"); |
||||||
|
} |
||||||
|
if((ACROBATICS & profs) != 0){ |
||||||
|
printf("Acrobatics\n"); |
||||||
|
} |
||||||
|
if((SLEIGHTOFHAND & profs) != 0){ |
||||||
|
printf("Sleight of Hand\n"); |
||||||
|
} |
||||||
|
if((STEALTH & profs) != 0){ |
||||||
|
printf("Stealth\n"); |
||||||
|
} |
||||||
|
if((ARCANA & profs) != 0){ |
||||||
|
printf("Arcana\n"); |
||||||
|
} |
||||||
|
if((HISTORY & profs) != 0){ |
||||||
|
printf("History\n"); |
||||||
|
} |
||||||
|
if((INVESTIGATION & profs) != 0){ |
||||||
|
printf("Investigation\n"); |
||||||
|
} |
||||||
|
if((NATURE & profs) != 0){ |
||||||
|
printf("Nature\n"); |
||||||
|
} |
||||||
|
if((RELIGION & profs) != 0){ |
||||||
|
printf("Regligion\n"); |
||||||
|
} |
||||||
|
if((ANIMALHANDLING & profs) != 0){ |
||||||
|
printf("Animal Handling\n"); |
||||||
|
} |
||||||
|
if((INSIGHT & profs) != 0){ |
||||||
|
printf("Insight\n"); |
||||||
|
} |
||||||
|
if((MEDICINE & profs) != 0){ |
||||||
|
printf("Medicine\n"); |
||||||
|
} |
||||||
|
if((PERCEPTION & profs) != 0){ |
||||||
|
printf("Perception\n"); |
||||||
|
} |
||||||
|
if((SURVIVAL & profs) != 0){ |
||||||
|
printf("Survival\n"); |
||||||
|
} |
||||||
|
if((DECEPTION & profs) != 0){ |
||||||
|
printf("Deception\n"); |
||||||
|
} |
||||||
|
if((INTIMIDATION & profs) != 0){ |
||||||
|
printf("Intimidation\n"); |
||||||
|
} |
||||||
|
if((PERFORMANCE & profs) != 0){ |
||||||
|
printf("Performance\n"); |
||||||
|
} |
||||||
|
if((PERSUASION & profs) != 0){ |
||||||
|
printf("Persuaion\n"); |
||||||
|
} |
||||||
|
|
||||||
|
printf("\nLanguages:\nCommon\n"); |
||||||
|
if((ABYSSAL & languages) != 0){ |
||||||
|
printf("Abyssal\n"); |
||||||
|
} |
||||||
|
if((CELESTIAL & languages) != 0){ |
||||||
|
printf("Celestial\n"); |
||||||
|
} |
||||||
|
if((DRACONIC & languages) != 0){ |
||||||
|
printf("Draconic\n"); |
||||||
|
} |
||||||
|
if((DEEPSPEECH & languages) != 0){ |
||||||
|
printf("Deep Speech\n"); |
||||||
|
} |
||||||
|
if((INFERNAL & languages) != 0){ |
||||||
|
printf("Infernal\n"); |
||||||
|
} |
||||||
|
if((PRIMORDIAL & languages) != 0){ |
||||||
|
printf("Primordial\n"); |
||||||
|
} |
||||||
|
if((SYLVAN & languages) != 0){ |
||||||
|
printf("Sylvan\n"); |
||||||
|
} |
||||||
|
if((UNDERCOMMON & languages) != 0){ |
||||||
|
printf("Undercommon\n"); |
||||||
|
} |
||||||
|
if((DWARVISH & languages) != 0){ |
||||||
|
printf("Dwarvish\n"); |
||||||
|
} |
||||||
|
if((ELVISH & languages) != 0){ |
||||||
|
printf("Elvish\n"); |
||||||
|
} |
||||||
|
if((GIANT & languages) != 0){ |
||||||
|
printf("Giant\n"); |
||||||
|
} |
||||||
|
if((GNOMISH & languages) != 0){ |
||||||
|
printf("Gnome\n"); |
||||||
|
} |
||||||
|
if((GOBLIN & languages) != 0){ |
||||||
|
printf("Goblin\n"); |
||||||
|
} |
||||||
|
if((HALFLISH & languages) != 0){ |
||||||
|
printf("Halfling\n"); |
||||||
|
} |
||||||
|
if((ORC & languages) != 0){ |
||||||
|
printf("Orcish\n"); |
||||||
|
} |
||||||
|
printf("\nOther Profs: %s\n", otherprofs); |
||||||
|
return 1; |
||||||
|
} |
@ -0,0 +1,97 @@ |
|||||||
|
// Define skill proficiencies for prof array
|
||||||
|
#define ATHLETICS 1 |
||||||
|
#define ACROBATICS 2 |
||||||
|
#define SLEIGHTOFHAND 4 |
||||||
|
#define STEALTH 8 |
||||||
|
#define ARCANA 16 |
||||||
|
#define HISTORY 32 |
||||||
|
#define INVESTIGATION 64 |
||||||
|
#define NATURE 128 |
||||||
|
#define RELIGION 256 |
||||||
|
#define ANIMALHANDLING 512 |
||||||
|
#define INSIGHT 1024 |
||||||
|
#define MEDICINE 2048 |
||||||
|
#define PERCEPTION 4096 |
||||||
|
#define SURVIVAL 8192 |
||||||
|
#define DECEPTION 16384 |
||||||
|
#define INTIMIDATION 32768 |
||||||
|
#define PERFORMANCE 65536 |
||||||
|
#define PERSUASION 131072 |
||||||
|
|
||||||
|
// Define language proficiences for prof array
|
||||||
|
#define ABYSSAL 1 |
||||||
|
#define CELESTIAL 2 |
||||||
|
#define DRACONIC 4 |
||||||
|
#define DEEPSPEECH 8 |
||||||
|
#define INFERNAL 16 |
||||||
|
#define PRIMORDIAL 32 |
||||||
|
#define SYLVAN 64 |
||||||
|
#define UNDERCOMMON 128 |
||||||
|
#define DWARVISH 256 |
||||||
|
#define ELVISH 512 |
||||||
|
#define GIANT 1024 |
||||||
|
#define GNOMISH 2048 |
||||||
|
#define GOBLIN 4096 |
||||||
|
#define HALFLISH 8192 |
||||||
|
#define ORC 16384 |
||||||
|
|
||||||
|
//Define values so we can make array references easier to read
|
||||||
|
#define BARBARIAN 0 |
||||||
|
#define BARD 1 |
||||||
|
#define CLERIC 2 |
||||||
|
#define DRUID 3 |
||||||
|
#define FIGHTER 4 |
||||||
|
#define MONK 5 |
||||||
|
#define PALADIN 6 |
||||||
|
#define RANGER 7 |
||||||
|
#define ROGUE 8 |
||||||
|
#define SORCERER 9 |
||||||
|
#define WARLOCK 10 |
||||||
|
#define WIZARD 11 |
||||||
|
|
||||||
|
#define DRAGONBORN 0 |
||||||
|
#define DWARF 1 |
||||||
|
#define ELF 2 |
||||||
|
#define GNOME 3 |
||||||
|
#define HALFELF 4 |
||||||
|
#define HALFLING 5 |
||||||
|
#define HALFORC 6 |
||||||
|
#define HUMAN 7 |
||||||
|
#define TIEFLING 8 |
||||||
|
|
||||||
|
#define ACOLYTE 0 |
||||||
|
#define CHARLATAN 1 |
||||||
|
#define CRIMINAL 2 |
||||||
|
#define ENTERTAINER 3 |
||||||
|
#define FOLKHERO 4 |
||||||
|
#define GUILDARTISAN 5 |
||||||
|
#define HERMIT 6 |
||||||
|
#define NOBLE 7 |
||||||
|
#define OUTLANDER 8 |
||||||
|
#define SAGE 9 |
||||||
|
#define SAILOR 10 |
||||||
|
#define SOLDIER 11 |
||||||
|
#define URCHIN 12 |
||||||
|
|
||||||
|
#define STR 0 |
||||||
|
#define DEX 1 |
||||||
|
#define CON 2 |
||||||
|
#define INT 3 |
||||||
|
#define WIS 4 |
||||||
|
#define CHA 5 |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
unsigned long profs; |
||||||
|
unsigned long languages; |
||||||
|
char otherprofs[100]; |
||||||
|
char* background; |
||||||
|
char* race; |
||||||
|
char* class; |
||||||
|
|
||||||
|
char* classes[] = {"Barbarian", "Bard", "Cleric", "Druid", "Fighter", "Monk", "Paladin", "Ranger", "Rogue", "Sorcerer", "Warlock", "Wizard" }; |
||||||
|
char* races[] = { "Dragonborn", "Dwarf", "Elf", "Gnome", "Half-Elf", "Halfling", "Half-Orc", "Human", "Tiefling" }; |
||||||
|
char* backgrounds[] = { "Acolyte", "Charlatan", "Criminal", "Entertainer", "Folk Hero", "Guild Artisan", "Hermit", "Noble", "Outlander", "Sage", "Sailor", "Soldier", "Urchin" }; |
||||||
|
int stats[6]; |
||||||
|
int mods[6]; |
||||||
|
int hp; |
@ -0,0 +1,302 @@ |
|||||||
|
#include <stdio.h> |
||||||
|
#include <stdarg.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
#include <getopt.h> |
||||||
|
#include <err.h> |
||||||
|
|
||||||
|
#include <sys/types.h> |
||||||
|
#include <sys/socket.h> |
||||||
|
#include <sys/select.h> |
||||||
|
#include <unistd.h> |
||||||
|
#include <netdb.h> |
||||||
|
|
||||||
|
void usage_and_die(const char *myname, const char *fmt, ...) |
||||||
|
{ |
||||||
|
if (fmt) |
||||||
|
{ |
||||||
|
va_list ap; |
||||||
|
va_start(ap, fmt); |
||||||
|
vwarnx(fmt, ap); |
||||||
|
} |
||||||
|
errx(EXIT_FAILURE, "Usage: %s [-4 | -6] [-u | -t] [-l listen-addr] [-p listen-port]", myname); |
||||||
|
} |
||||||
|
|
||||||
|
enum { MAX_LISTENERS = 8, MAX_CONNECTED = 64 }; |
||||||
|
int listen_fds[MAX_LISTENERS]; |
||||||
|
int packet_fds[MAX_LISTENERS]; |
||||||
|
int stream_fds[MAX_CONNECTED]; |
||||||
|
unsigned num_listen, num_stream, num_packet; |
||||||
|
|
||||||
|
int main(int argc, char **argv) |
||||||
|
{ |
||||||
|
int opt; |
||||||
|
int ret; |
||||||
|
|
||||||
|
char hbuf[1024]; |
||||||
|
char sbuf[32]; |
||||||
|
|
||||||
|
struct addrinfo hints = {0}; |
||||||
|
struct addrinfo *bind_addrs; |
||||||
|
|
||||||
|
const char *listen_addr = NULL; |
||||||
|
const char *listen_port = NULL; |
||||||
|
|
||||||
|
hints.ai_flags = AI_PASSIVE; |
||||||
|
hints.ai_family = AF_UNSPEC; |
||||||
|
|
||||||
|
while ((opt = getopt(argc, argv, "46l:p:tu")) != -1) |
||||||
|
{ |
||||||
|
switch (opt) |
||||||
|
{ |
||||||
|
case '4': |
||||||
|
case '6': |
||||||
|
if (hints.ai_family != AF_UNSPEC) |
||||||
|
usage_and_die(argv[0], "-4 and -6 are incompatible (leave out to use both)"); |
||||||
|
hints.ai_family = (opt == '4') ? AF_INET : AF_INET6; |
||||||
|
break; |
||||||
|
case 't': |
||||||
|
case 'u': |
||||||
|
if (hints.ai_protocol != 0) |
||||||
|
usage_and_die(argv[0], "-t and -u are incompatible (leave out to use both)"); |
||||||
|
hints.ai_protocol = (opt == 't') ? IPPROTO_TCP : IPPROTO_UDP; |
||||||
|
break; |
||||||
|
case 'l': |
||||||
|
if (listen_addr != NULL) |
||||||
|
usage_and_die(argv[0], "Only one listen address allowed!"); |
||||||
|
listen_addr = optarg; |
||||||
|
break; |
||||||
|
case 'p': |
||||||
|
if (listen_port != NULL) |
||||||
|
usage_and_die(argv[0], "Only one listen port allowed!"); |
||||||
|
listen_port = optarg; |
||||||
|
break; |
||||||
|
default: |
||||||
|
usage_and_die(argv[0], NULL); |
||||||
|
/* not reached */ |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
argc -= optind; |
||||||
|
argv += optind; |
||||||
|
|
||||||
|
if (argc != 0) |
||||||
|
usage_and_die(argv[0], NULL); |
||||||
|
|
||||||
|
if (listen_port == NULL) |
||||||
|
listen_port = "chargen"; |
||||||
|
|
||||||
|
printf("Opening listeners on %s:%s...\n", (listen_addr == NULL) ? "*" : listen_addr, listen_port); |
||||||
|
ret = getaddrinfo(listen_addr, listen_port, &hints, &bind_addrs); |
||||||
|
if (ret) |
||||||
|
errx(EXIT_FAILURE, "getaddrinfo: %s", gai_strerror(ret)); |
||||||
|
for (struct addrinfo *a = bind_addrs; a != NULL; a = a->ai_next) |
||||||
|
{ |
||||||
|
int s; |
||||||
|
|
||||||
|
ret = getnameinfo(a->ai_addr, a->ai_addrlen, hbuf, sizeof hbuf, sbuf, sizeof sbuf, NI_NUMERICHOST | NI_NUMERICSERV | (a->ai_socktype == SOCK_STREAM) ? 0 : NI_DGRAM); |
||||||
|
if (ret) |
||||||
|
warn("getnameinfo: %s", gai_strerror(ret)); |
||||||
|
else |
||||||
|
printf("Opening %s listener on %s:%s...\n", (a->ai_socktype == SOCK_STREAM) ? "stream" : "packet", hbuf, sbuf); |
||||||
|
|
||||||
|
s = socket(a->ai_family, a->ai_socktype, a->ai_protocol); |
||||||
|
if (s == -1) |
||||||
|
{ |
||||||
|
warn("socket"); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (bind(s, a->ai_addr, a->ai_addrlen) == -1) |
||||||
|
{ |
||||||
|
warn("bind: %s:%s", hbuf, sbuf); |
||||||
|
close(s); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (a->ai_socktype == SOCK_STREAM) |
||||||
|
{ |
||||||
|
if (listen(s, 128) == -1) |
||||||
|
{ |
||||||
|
warn("listen"); |
||||||
|
close(s); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (num_stream >= MAX_LISTENERS) |
||||||
|
{ |
||||||
|
warnx("Listener table full, discarding %s:%s", hbuf, sbuf); |
||||||
|
close(s); |
||||||
|
} |
||||||
|
else |
||||||
|
listen_fds[num_listen++] = s; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if (num_packet >= MAX_LISTENERS) |
||||||
|
{ |
||||||
|
warnx("Listener table full, discarding %s:%s", hbuf, sbuf); |
||||||
|
close(s); |
||||||
|
} |
||||||
|
else |
||||||
|
packet_fds[num_packet++] = s; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (num_packet + num_listen == 0) |
||||||
|
errx(EXIT_FAILURE, "No listeners to run on!\n"); |
||||||
|
|
||||||
|
if (pledge("stdio inet error", NULL) == -1) |
||||||
|
warn("pledge"); |
||||||
|
|
||||||
|
while(1) |
||||||
|
{ |
||||||
|
fd_set r, w; |
||||||
|
int maxfd = 0; |
||||||
|
|
||||||
|
union |
||||||
|
{ |
||||||
|
struct sockaddr_in a4; |
||||||
|
struct sockaddr_in6 a6; |
||||||
|
} addr; |
||||||
|
/* 64k = max UDP payload size */ |
||||||
|
static unsigned char data[64 * 1024]; |
||||||
|
|
||||||
|
FD_ZERO(&r); |
||||||
|
FD_ZERO(&w); |
||||||
|
|
||||||
|
if (num_stream < MAX_CONNECTED) |
||||||
|
{ |
||||||
|
for (unsigned i = 0; i < num_listen; i++) |
||||||
|
{ |
||||||
|
FD_SET(listen_fds[i], &r); |
||||||
|
if (listen_fds[i] > maxfd) |
||||||
|
maxfd = listen_fds[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
for (unsigned i = 0; i < num_packet; i++) |
||||||
|
{ |
||||||
|
FD_SET(packet_fds[i], &r); |
||||||
|
if (packet_fds[i] > maxfd) |
||||||
|
maxfd = packet_fds[i]; |
||||||
|
} |
||||||
|
for (unsigned i = 0; i < num_stream; i++) |
||||||
|
{ |
||||||
|
FD_SET(stream_fds[i], &r); |
||||||
|
FD_SET(stream_fds[i], &w); |
||||||
|
if (stream_fds[i] > maxfd) |
||||||
|
maxfd = stream_fds[i]; |
||||||
|
} |
||||||
|
|
||||||
|
ret = select(maxfd+1, &r, &w, NULL, NULL); |
||||||
|
if (ret == -1) |
||||||
|
err(EXIT_FAILURE, "select"); |
||||||
|
if (ret == 0) |
||||||
|
{ |
||||||
|
printf("Huh, that's weird, select says nothing ready but I didn't ask for a timeout\n"); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
for (unsigned i = 0; i < num_listen; i++) |
||||||
|
{ |
||||||
|
int l = listen_fds[i]; |
||||||
|
if (FD_ISSET(l, &r)) |
||||||
|
{ |
||||||
|
struct sockaddr *sa = (struct sockaddr *)&addr; |
||||||
|
socklen_t len = sizeof addr; |
||||||
|
int s = accept(l, sa, &len); |
||||||
|
if (s == -1) |
||||||
|
{ |
||||||
|
warn("accept"); |
||||||
|
continue; |
||||||
|
} |
||||||
|
stream_fds[num_stream++] = s; |
||||||
|
|
||||||
|
ret = getnameinfo(sa, len, hbuf, sizeof hbuf, sbuf, sizeof sbuf, NI_NUMERICHOST | NI_NUMERICSERV); |
||||||
|
if (ret) |
||||||
|
warn("getnameinfo: %s", gai_strerror(ret)); |
||||||
|
else |
||||||
|
printf("Accepting stream connection from %s:%s...\n", hbuf, sbuf); |
||||||
|
} |
||||||
|
|
||||||
|
if (num_stream >= MAX_CONNECTED) |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
for (unsigned i = 0; i < num_packet; i++) |
||||||
|
{ |
||||||
|
int s = packet_fds[i]; |
||||||
|
if (FD_ISSET(s, &r)) |
||||||
|
{ |
||||||
|
ssize_t rval; |
||||||
|
|
||||||
|
struct sockaddr *sa = (struct sockaddr *)&addr; |
||||||
|
socklen_t len = sizeof addr; |
||||||
|
|
||||||
|
rval = recvfrom(s, data, sizeof data, 0, sa, &len); |
||||||
|
if (rval == -1) |
||||||
|
{ |
||||||
|
warn("recvfrom"); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
ret = getnameinfo(sa, len, hbuf, sizeof hbuf, sbuf, sizeof sbuf, NI_NUMERICHOST | NI_NUMERICSERV); |
||||||
|
if (ret) |
||||||
|
warn("getnameinfo: %s", gai_strerror(ret)); |
||||||
|
else |
||||||
|
printf("Responding to packet from %s:%s...\n", hbuf, sbuf); |
||||||
|
|
||||||
|
rval = sendto(s, "chargen data\r\n", 14, 0, sa, len); |
||||||
|
if (rval == -1) |
||||||
|
warn("sendto"); |
||||||
|
} |
||||||
|
} |
||||||
|
for (unsigned i = 0; i < num_stream; i++) |
||||||
|
{ |
||||||
|
int s = stream_fds[i]; |
||||||
|
if (FD_ISSET(s, &r)) |
||||||
|
{ |
||||||
|
ssize_t rval; |
||||||
|
rval = read(s, data, sizeof data); |
||||||
|
if (rval <= 0) |
||||||
|
{ |
||||||
|
if (rval == -1) |
||||||
|
warn("read"); |
||||||
|
printf("Closing stream connection\n"); |
||||||
|
close(s); |
||||||
|
|
||||||
|
/*
|
||||||
|
* We will miss checking the socket we move down to fill the gap we're leaving. |
||||||
|
* That's OK, if it's ready we'll get it next time. |
||||||
|
*/ |
||||||
|
stream_fds[i] = stream_fds[--num_stream]; |
||||||
|
continue; |
||||||
|
} |
||||||
|
/* Ignore data from a successful read */ |
||||||
|
} |
||||||
|
if (FD_ISSET(s, &w)) |
||||||
|
{ |
||||||
|
if (s == 0) |
||||||
|
{ |
||||||
|
printf("WTF? i=%u num_stream=%u stream_fds[i]=%d s=%d\n", i, num_stream, stream_fds[i], s); |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (write(s, "chargen data\r\n", 14) == -1) |
||||||
|
{ |
||||||
|
warn("write"); |
||||||
|
printf("Closing stream connection\n"); |
||||||
|
close(s); |
||||||
|
|
||||||
|
/*
|
||||||
|
* We will miss checking the socket we move down to fill the gap we're leaving. |
||||||
|
* That's OK, if it's ready we'll get it next time. |
||||||
|
*/ |
||||||
|
stream_fds[i] = stream_fds[--num_stream]; |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* not reached */ |
||||||
|
} |
Loading…
Reference in new issue